#
##
### MoThEr. Mommas gonna make all of your nightmares come true.
##                                                        PF
#

"""
Where the main Mother Classes are defined:

 * DbMother
 * MotherManager
 * MotherBox
"""
            
from commons import ERR_COL, INF_COL, OKI_COL
from dbda import Psygres


#
##
### Mother Symbols
##
#

#
## MoThEr: init flags.
#

MO_NOA    = 0     # No Action
MO_DEL    = 1     # Del Action
MO_UP     = 2     # Update Action
MO_SAVE   = 3     # Save Action
MO_LOAD   = 4     # Load Action
MO_ULOAD  = 5     # Unsafe Load Action

MO_BEFORE = 0
MO_AFTER  = 1

#
## MoThEr: SQL symbols.
#

SQL_DEFAULT = "MOTHER_SQL_DEFAULT"
SQL_NULL    =  None
SQL_TRUE    = "True"
SQL_FALSE   = "False"


_J= ', '.join
_A= ' AND '.join

#
##
### MoThEr Utils.
##
#

def init_mother(cfile, pg_conn= None, fnaming= None):

    if not hasattr(DbMother, '_mo_initialized'):
        from speaker import Speaker
        Speaker.log_warning("Mother already initialized.")
        return

    if not fnaming:
        from commons import MotherNaming
        fnaming= MotherNaming

    d= {}
    import speaker
    execfile(cfile, speaker.__dict__, d)

    # Handle OIDs or not?
    use_oids= d.get('MOTHER_OIDS', True)
    if not use_oids:
        from dbda import PsygresFly
        del DbMother._sqlPkeysFromOid
        PsygresFly.exported_methods.remove('lastrowid')
        _sqli= DbMother._sqlInsertNoOid
        _i= DbMother._insertNoOid
    else:
        _sqli= DbMother._sqlInsertOid
        _i= DbMother._insertOid

    sname= d.get('MOTHER_SESSION_NAME', DbMother._mo_session_name)

    from dbda import init_dbda

    speaker.init_speaker(d)
    init_dbda(d, pg_conn)

    # Load DB map
    momap= d['MOTHER_MAP']
    _DbMap._load_map(momap)

    # Design Mother
    DbMother.MotherNaming= staticmethod(fnaming)
    DbMother._mo_cfile= cfile
    DbMother._oided_mother= use_oids
    DbMother.__init__= DbMother.__post_init__
    DbMother._sqlInsert= _sqli
    DbMother._insert= _i
    DbMother._mo_session_name= sname

    # Ohhh, destroying is amazing, isn't it?
    del DbMother.__post_init__
    del DbMother._sqlInsertOid
    del DbMother._sqlInsertNoOid
    del DbMother._insertOid
    del DbMother._insertNoOid
    del DbMother._mo_initialized


def MotherSession(name= None, pg_conn= None):

    from dbda import PsygresPool

    if not name:
        import random
        name= "%s%d" % (DbMother._mo_session_name, random.randint(1,10000))

    return PsygresPool.newSession(name, pg_conn) 



class _DbMap(Psygres):
    _map_file=       None
    _map_fields=     None
    _map_pkeys=      None
    _map_children=   None
    _map_rels=       None

    @staticmethod
    def _load_map(map_file):
        from speaker import Speaker
        from os import path
        if not path.isfile(map_file):
            Speaker.log_int_raise("Mother Map %s does not exist.", ERR_COL(map_file)) 
        
        try:
            fil= open(map_file)
        except:
            Speaker.log_int_raise("Unable to open Mother Map %s.", ERR_COL(map_file)) 

        import cPickle
        try:
            map_dicts= cPickle.load(fil)
            _DbMap._map_file= map_file
            _DbMap._map_fields= map_dicts['K']
            _DbMap._map_pkeys= map_dicts['P']
            _DbMap._map_children= map_dicts['C']
            _DbMap._map_rels= map_dicts['R']
        except:
            try:
                fil.close()
            except:
                pass

            Speaker.log_int_raise("Mother Map %s is malformed.", ERR_COL(map_file)) 

        fil.close()

    @staticmethod
    def _table_fields(tbl):
        return _DbMap._map_fields[tbl]

    @staticmethod
    def _table_pkeys(tbl):
        return _DbMap._map_pkeys[tbl]

    @staticmethod
    def _table_children(tbl):
        return _DbMap._map_children[tbl]

    @staticmethod
    def _searchRelation(tbls):

        from speaker import Speaker
        fz= frozenset(tbls)

        trd= _DbMap._map_rels
        try:
            r_tbl= trd[fz]
            return r_tbl

        except:
            pass

        Speaker.log_insane(\
            "Warning: direct relation for tables %s "
            "is not defined: searching subset...",
            INF_COL(list(fz)))

        rels= [trd[k] for k in trd.keys() if fz< k]

        if len(rels)<>1:
            Speaker.log_int_raise(
                "In _searchRelation: can not get the "  
                "relation table. There are %s possible "     
                "choices: %s .", ERR_COL(len(rels)), 
                ERR_COL(_J(rels)))

        return rels[0]

    @staticmethod
    def _isChildOf(c, f):
        return c in _DbMap._map_children[f]

def _do_nothing(*args):
    pass

#
##
### The Mother Class
##
#

class DbMother(_DbMap):
    """ The Holy MoThEr 
        An abstract class which represents a db Table.
    """
    _mo_initialized= False
    _mo_cfile= None
    _mo_false_values= [SQL_NULL, SQL_DEFAULT]
    _mo_fuck= " MoThEr fucker!"
    _mo_session_name= "MoSession-"
    _mo_flags= { 
                MO_NOA: 'MO_NOA', 
                MO_DEL: 'MO_DEL', 
                MO_UP: 'MO_UP', 
                MO_SAVE: 'MO_SAVE', 
                MO_LOAD: 'MO_LOAD', 
                MO_ULOAD: 'MO_ULOAD'
                    }
        
    # Mapping actions
    @staticmethod
    def _flag_actions(obj, flag):
        if flag == MO_UP: return obj.update
        if flag == MO_DEL: return obj.delete
        if flag == MO_NOA: return _do_nothing
        if flag == MO_SAVE: return obj.insert
        if flag == MO_LOAD: return obj.load
        if flag == MO_ULOAD: return obj.uload 
        obj.log_int_raise("Invalid flag %s", ERR_COL(flag))

    @staticmethod
    def _trigger_actions(obj, flag):
        if flag == MO_UP: return obj._update
        if flag == MO_DEL: return obj._delete
        if flag == MO_SAVE: return obj._insert
        if flag == MO_LOAD: return obj._load
        obj.log_int_raise("Invalid flag %s", ERR_COL(flag))

    def __init__(self, store= {}, flag= MO_NOA, session= None):
        """ __init__(dict_values, flag, session)
        """

        self.log_int_raise(ERR_COL("Mother not Initialized yet."))

    def __post_init__(self, store= {}, flag= MO_NOA, session= None):
        """ __init__(dict_values, flag, session)
        """

        # table_name is needed...
        if not hasattr(self, 'table_name'):
            self.log_int_raise("No table_name defined for this Mother.")

        # Useful var
        tbl= self.table_name

        # self.fields is a list with TABLE fields.
        # self.pkeys is a list with primary keys for the TABLE.
        # You can set these vars in the child class or let
        # MoThEr assign them dynamically.
        if not hasattr(self, "fields"):
            self.fields = self._table_fields(tbl)
           
        if not hasattr(self, "pkeys"):
            self.pkeys = self._table_pkeys(tbl)

        # Frozeset!
        self.fields= frozenset(self.fields)
        self.pkeys= frozenset(self.pkeys)

        # Session() ?
        if session:
            session._import(self)
        self.session= session

        # Storing dict values with some controls depending on the 
        # initialization flag....
        self._initStore(store, flag)
        # ... and enjoy ;)

    def initTriggers(self):
        """ initTriggers() -> None

        Initialize triggers. Don't call it directly. Use
        addTrigger instead.\n"""

        self.triggers_map= {}
        t= self.triggers_map
            
        for flag in [MO_UP, MO_LOAD, MO_SAVE, MO_DEL]:
            t[flag]= {}
            for when in [MO_AFTER, MO_BEFORE]:
                t[flag][when]= []

        self._trigger_initialized= True

    def _initStore(self, store, flag):
        """ _initStore(store, flag) --> None

        Inizialization of self._store depending on flag.
        """

        # Controls: trivial
        setK= set(store.keys())
        accio= self._invalidFields(setK)
        if accio:
            self.log_int_raise("Invalid Fields %s for table |%s|.",\
                                ERR_COL(list(accio)), self.table_name)

        self._moved= set([])
        self._store= store.copy()

        # Signal Mother that passed fields are
        # new fields 
        self._moved= set([k for k in setK if k not in self.pkeys])

        self._flag_actions(self, flag)()

#
##
### Internal Core
##
#
    def _triggers_are_initialized(self):
        return hasattr(self, 'triggers_map')

    def _unvaluedPKeys(self):
        """ _unvaluedPKeys() --> set

        Returns the set of pkeys not valu'ed.
        NOTE: a pkeys is considere NOT VALUED if his value is 
        DEFAULT or NULL.
        """

        s=self._store
        return set([\
            k for k in self.pkeys\
            if not s.has_key(k)\
            or s[k] in self._mo_false_values])

    def _noPKeyFields(self):
        """ _noPKeyFields() --> set

        Returns the list of fields in store that are not pkeys.
        """
        return set(self._store.keys()) - self.pkeys

    def _invalidFields(self,fields):
        """ _invalidFields(fields) --> set

        field in fields are returned if they are not present
        in self.fields.
        """

        if type(fields) is not set:
            fields=set(fields)
        return fields - self.fields

    def _fieldsMissing(self):
        """ _fieldsMissing() --> set

        Returns the list of table fields not valu'ed yet.
        """
        return self.fields - set(self._store.keys())

    def __str__(self):
        """ __str__() --> string 
        """
        u= INF_COL("Unset")
        res= "  Table |%s|\n\n" % self.table_name
        sget= self._store.get
        for k in self.fields:
            val= sget(k,u)
            sep= k in self._moved and '*' or '~'
            res+= ("  %12s %s %s\n" % (k,sep,str(val)))
        return res

#
##
### Sql Formatting
##
#

    def _sqlPkeysFromOid(self, oid):
        if not len(self.pkeys):
            return
        qry= self._sqlSelect(self.pkeys, 'oid= %d' % oid)
        res= self.or_query(qry)
        self._store.update(res)

    def _sqlWherePkeys(self, params= None):
        res= []

        params= params and set(params) or set()

        for k in self.pkeys | params:
            res.append('%s= %%(%s)s' % (k, k))

        res= _A(res)
        return self._sqlMogrify(res)

    def _sqlUpdate(self, d, params= None):

        res= []
        s= self._store

        for k in d:
            v= s[k]
            if v== SQL_DEFAULT:
                res.append('%s= DEFAULT' % k)
            else:
                res.append('%s= %%(%s)s' % (k, k))

        qry= ("UPDATE %s SET %s WHERE %s" % (self.table_name, 
                _J(res), self._sqlWherePkeys(params)))

        return self.mogrify(qry, s)

    def _sqlInsertNoOid(self, d):

        s= self._store

        res= []
        for k in d:
            v= s[k]
            if v== SQL_DEFAULT:
                res.append('DEFAULT')
            else:
                res.append('%%(%s)s' % k)

        qry= ("INSERT INTO %s (%s) VALUES (%s) " % 
                (self.table_name, _J(d), _J(res)))

        if len(self.pkeys):
            qry+="RETURNING %s" % _J(self.pkeys)

        return self._sqlMogrify(qry)

    def _sqlInsertOid(self, d):

        s= self._store

        res= []
        for k in d:
            v= s[k]
            if v== SQL_DEFAULT:
                res.append('DEFAULT')
            else:
                res.append('%%(%s)s' % k)

        qry= ("INSERT INTO %s (%s) VALUES (%s)" % 
                (self.table_name, _J(d), _J(res)))

        return self._sqlMogrify(qry)

    def _sqlDelete(self, params= None):

        filter= self._sqlWherePkeys(params)
        return ("DELETE FROM %s WHERE %s" % 
                (self.table_name, filter))

    def _sqlSelect(self, d, filter= None, tbl= None):

        if filter is None:
            filter= self._sqlWherePkeys()

        tbl= tbl or self.table_name
        what= ['%s.%s' % (tbl, f) for f in d]

        return ("SELECT %s FROM %s WHERE %s" % 
                (_J(what), tbl, filter ))


    def _sqlBuildFilter(self, filter, prefix= False, tbl= None):
        """ _sqlBuildFilter(self, filter [,prefix, tbl]) --> str

        Build the filter as SQL string. filter could be
        a string, a dict or a list (in such case use internal store).
        If prefix, WHERE is added (unless it's present yet).
        With tbl you can specify where apply the filter.
        """

        def t(tp):
            return isinstance(filter, tp)

        tbl= tbl or self.table_name

        if t(str):
            s= filter

        elif t(dict):
            res= []
            for k, v in filter.iteritems():
                if v== SQL_DEFAULT:
                    res.append('%s.%s= DEFAULT' % (tbl, k))
                else:
                    res.append('%s.%s= %%(%s)s' % (tbl, k, k))
            s= self.mogrify(_A(res), filter)

        elif t(set) or t(frozenset) or t(list):
            res= []
            for k in filter:
                v= self._store[k]
                if v== SQL_DEFAULT:
                    res.append('%s.%s= DEFAULT' % (tbl, k))
                else:
                    res.append('%s.%s= %%(%s)s' % (tbl, k, k))
            s=self._sqlMogrify(_A(res))

        else:
            self.log_int_raise(\
                    "Invalid filter type: %s.",\
                    ERR_COL(str(filter)))

        # Prevent Double WHERE
        if not prefix or 'WHERE' in s:
            return s

        return " WHERE %s " % s 

    def _sqlMogrify(self, s, d= None):
        """ A wrapper to dbda.mogrify that take
        self._store as default dict value.
        """
        return self.mogrify(s, d or self._store)

#
##
### Core
##
#


    def get_triggers(self, flag, when):
        """ get_triggers(flag, when) -> list(functions)

        Returns triggers.
        """

        if not self._triggers_are_initialized():
            self.log_int_raise("Trigger are not initialized.")

        t= self.triggers_map
        try:
            d= t[flag]
        except:
            self.log_int_raise("get_trigger(): invalid flag %s.", ERR_COL(flag))

        try:
            l= d[when]
        except:
            self.log_int_raise("get_trigger(): invalid time %s.", ERR_COL(when))

        return l

    def get_flag_triggers(self, flag):
        """ get_flag_triggers(flag) -> list(functions)

        Returns triggers.
        """
        return  self.get_triggers(flag, MO_BEFORE) + \
                self.get_triggers(flag, MO_AFTER)

    def has_trigger(self, flag):
        """ has_trigger(flag) -> bool

        Is there a trigger for the flag `flag`?
        """
        if not self._triggers_are_initialized():
            return False

        return len(self.get_flag_triggers(flag)) > 0

    def add_trigger(self, flag, when, trigger):
        """ add_trigger(flag, when, f) --> None

        flag= MO_LOAD, MO_SAVE, MO_DEL, MO_UP
        when= MO_AFTER, MO_BEFORE

        f is the trigger: a python function
        """

        if not self._triggers_are_initialized():
            self.initTriggers()

        self.triggers_map[flag][when].append(trigger)

    def trigger(self, flag, when, *args):
        """ trigger(flag, when, *args) --> None

        flag= MO_LOAD, MO_SAVE, MO_DEL, MO_UP
        when= MO_AFTER, MO_BEFORE

        execute the tasked triggers for action flag and when
        """

        if not self._triggers_are_initialized():
            self.log_int_raise("Triggers are not initialized.")

        l= self.get_triggers(flag, when)
        when_str= when == MO_AFTER and 'After' or 'Before'
        for f in l:
            f(*args)
            self.log_info("Trigger %s (%s-%s) Fired!", 
                    OKI_COL(f.func_name), when_str, self._mo_flags[flag])

    def setField(self, field_name, field_value):
        """ setField(field_name, field_value) --> None

        set field. If field is an invalid field, CRASH.
        """

        # Pkeys cannot be changed! It's a non-sense.
        # If you want insert a new record, create new MoThEr 
        # If you want to load a different record, use refresh()
        if field_name in self.pkeys:
            self.log_int_raise(
                " Safe behaviour: you can not change Pkeys Value. "\
                "Want to load a new record? Use refresh() "\
                "Want to update a Pkey? Create new and delete old."
                )

        if field_name not in self.fields:
            self.log_int_raise("Mth: cannot setField(): field "\
                    "%s is invalid.",ERR_COL(field_name))

        # Ok, new value is good
        self._store[field_name]=field_value
        # Remember that a field is changed.
        self._moved.add(field_name)

    def setFields(self,fdict,safe_mode=True):
        """ setFields(fdict [,safe_mode= True]) --> None

        Update the store with fdict. 
        If `safe_mode`, it will be forbidden to change 
        primary keys value. Really, use safe_mode iff
        you know exactly what are you doing.

        """
        # Is fdict a valid dict?
        new_fields=set(fdict.keys())
        onzo=self._invalidFields(new_fields)
        if onzo:
            self.log_int_raise("Mth: cannot setFields(): "\
                    "invalid fields %s.", ERR_COL(str(onzo)))

        # Cannot change pkeys
        if safe_mode:
            sp=self.pkeys
            if sp & new_fields:
                self.log_int_raise(
                    "For your safety, you can not change Pkeys Value. "
                    "Want to load a new record? Use refresh(). "
                    "Want to update a Pkey? Create new and delete old. "
                    "If you know what you do, use safe_mode=False. "
                    )

        # Ok, new values are good.
        self._store.update(fdict)
        # Remember fields changed.
        self._moved |= new_fields

    def getFields(self, fields= None, autoload= False):
        """ getFields([fields=None,autoload=False]) --> dict

        Returns stored values.
        If fields is not None, returns values for fields specified.
        If a field is invalid, CRASH.
        If a field is unvalued:
            if autload: load
            if not autoload: CRASH
        """

        if fields is None:
            return self._store.copy()

        onzo=self._invalidFields(fields)
        if onzo:
            self.log_int_raise("Mth: cannot setFields(): "\
                    "invalid fields %s.", ERR_COL(str(onzo)))
        newd={}
        if autoload:
            skeys=set(self._store.keys())
            nkeys=set(fields)
            lkeys=nkeys-skeys
            if len(lkeys):
                self.log_insane("Autoloading fields %s...", INF_COL(lkeys))
                self.load(lkeys)
            
        for f in fields:
            try:
                val=self._store[f]
                newd[f]=val
            except:
                self.log_int_raise(\
                    "Mth: field %s is not valued.",\
                        ERR_COL(f))
        return newd

    def _insertOid(self):

        s= self._store

        # Useful vars
        sk= s.keys()

        # Make the query ...
        qry= self._sqlInsert(sk)

        self.oc_query(qry)

        # get last oid (for INSERT only)
        oid= self.lastrowid()
        self._sqlPkeysFromOid(oid)

        # Nothing has to be updated now.
        self._moved.clear()

    def _insertNoOid(self):

        s= self._store

        # Useful vars
        sk= s.keys()

        # Make the query ...
        qry= self._sqlInsert(sk)

        if len(self.pkeys):
            res= self.or_query(qry)
            self._store.update(res)
        else:
            self.oc_query(qry)

        # Nothing has to be updated now.
        self._moved.clear()

    def _update(self):

        # Really need to update?
        if not self._moved:
            self.log_int_raise("Mth: nothing to update.")

        ange= self._unvaluedPKeys()
        if ange:
            from eccez import UpdateError
            self.log_raise("Refusing to UPDATE record "\
                           "without primary key (missing %s).",
                           ERR_COL(list(ange)), UpdateError)

        params= (set(self._store) - set(self._moved)) 

        # Make the query ...
        qry= self._sqlUpdate(self._moved, params)

        # and execute it
        try:
            self.oc_query(qry)
        except QueryError,ange:
            from eccez import UpdateError
            self.log_raise(str(ange), UpdateError)

        # Debugging infos...
        self.log_insane( "Mth: fields %s moved ;)",
                OKI_COL(self._moved))

        # Nothing has to be updated now.
        self._moved.clear()

    def _delete(self):

        # The values in store act as filters
        # when deleting
        ange= self._unvaluedPKeys()
        if ange:
            from eccez import DeleteError
            self.log_raise("Refusing to DELETE record "\
                           "without primary key (missing %s).",
                           ERR_COL(list(ange)), DeleteError)

        # the store acts as a filter..
        params= set(self._store.keys())

        # Make the query ...
        qry= self._sqlDelete(params)

        # and execute it
        try:
            self.oc_query(qry)
        except QueryError, ange:
            from eccez import DeleteError
            self.log_raise(str(ange), DeleteError)

        # Delete all informations...  
        self._moved.clear()
        self._store.clear()

    def _load(self, fields= None, safe_mode= True):

        # Which fields we need?
        mf= fields and set(fields) or set(self._fieldsMissing())

        if not mf <= self.fields:
            self.log_int_raise("Invalid Fields %s for table %s.",
                    ERR_COL(list(mf - self.fields)), self.table_name)

        if not len(mf):
            self.log_warning("No fields specified for SELECT: table %s",
                        ERR_COL(self.table_name))
            return self.getFields()

        if safe_mode:
            ange= self._unvaluedPKeys()
            if ange:
                from eccez import SelectError
                self.log_raise("Refusing to LOAD record "\
                               "without primary key (missing %s). "
                               "Use ULOAD to avoid this limit.",
                               ERR_COL(list(ange)), SelectError)

        filter= self._sqlBuildFilter(self._store)

        # Make the query ...
        qry= self._sqlSelect(mf, filter= filter)

        # and execute it
        try:
            res= self.or_query(qry)
        except Exception, ange:
            from eccez import SelectError
            self.log_raise("Unable to SELECT: %s", str(ange), SelectError)

        # Update values on store
        self._store.update(res)

        # Forgot old to-update fields  
        self._moved-= mf

        # Useful return, that's all
        return res

    def _triggered_action(self, flag, *args):

        f= self._trigger_actions(self, flag)

        if not self._triggers_are_initialized() or \
                not self.get_flag_triggers(flag):

            res= f(*args)

        else:
            self.beginTrans()
            try:
                self.log_insane(
                        "Launching %s triggers...",
                        OKI_COL('before'))
                self.trigger(flag, MO_BEFORE)

                self.log_insane( 
                        "Executing %s action...",
                        OKI_COL(self._mo_flags[flag]))

                res= f(*args)

                self.log_insane( 
                        "Launching %s triggers...",
                        OKI_COL('after'))

                self.trigger(flag, MO_AFTER)

            except Exception, s:
                self.rollback()
                self.log_int_raise(str(s))

            self.commit()

        self.log_info("Action %s.", OKI_COL("completed"))
        return res

    def delete(self):
        """ delete() --> None

        Really do you want to kill me?
        """
        self._triggered_action(MO_DEL)
    
    def load(self, fields= None, safe_mode= True):
        """ load([fields= None, safe_mode= True]) --> dict

        Retrieve record values from DB. 
        if fields is not None, only fields specified will
        be loaded.
        If safe_mode is False, try to load the record also if
        primary keys was not given.
        """
        self._triggered_action(MO_LOAD,fields,safe_mode)

    def insert(self):
        """ insert() --> None

        Insert to db the values stored in store.
        For field with no value, SQL_DEFAULT is assumed.
        """
        self._triggered_action(MO_SAVE)
        
    def update(self, updict= None):
        """ update([updict= None]) --> None

        Make a UPDATE statement for modified fields and/or updict.
        If updict is None, only fields setted with setField(s) are
        moved. Otherwise, also fields specified in the dict updict 
        are considered.
        """
        if updict:
            self.setFields(updict)

        self._triggered_action(MO_UP)

    def uload(self, fields= None):
        """ uload([fields= None]) --> dict

        This is a call to load() with safe_mode= False
        """

        return\
            self.load(fields,safe_mode=False)

    def refresh(self, d, flag= MO_NOA):
        """ refresh(d[,flag=MO_NOA]) --> None

        You can secify here a new set of pkeys and
        have a new, fresh mother
        """
        # init store.
        self._initStore(d, flag)


class MotherFly(DbMother):
    def __init__(self, tbl):
        self.table_name= tbl

    def __call__(self, d= {}, flag= MO_NOA, session= None):
        DbMother.__init__(self, d, flag, session)
        return self


def getMotherBuilder(tbl):
    """ getMotherBuilder(tbl_name) --> class

    Returns a `MoThEr Builder On The Fly` Class.
    You can init the class with a simple call:

        momma= getMotherBuilder(table)
        momma(mydict, myflag)
    """
    momma= MotherFly(tbl)
    return momma


def getMotherObj(tbl, d={}, flag= MO_NOA, session= None):
    """ getMotherObj(tbl_name[, diz= {}, flag= MO_NOA, session= None]) --> obj

    Initialize and return a `MoThEr On The Fly` Instance.
    """
    momma= getMotherBuilder(tbl)
    momma(d, flag, session)
    return momma

#
##
### The MoThEr Manager
##
#
#
##
###
#
# You can use the manager plugin to handle a set
# of child records, and to handle relations in a 
# magic way
# To use it, specify the dependencies:
#
#   class Foo(DbMother,MotherManager):
#       def __init__(self,.....):
#           DbMother.__init__(self,...
#
# but don't init the manager.
# You will have a set of useful functions.
# If you want more magic, you can run:
#
#   initChildManager(children_list)
#   initRelationManager(children_list)
#   initListManager(children_list)
#
###
##
#



class MotherManager:
    """ Mother Manager-Plugin. Need Mother."""

#
##
### Internal use.
##
#

    def _sqlJoin(self, j_tbl, r_tbl, j_key, r_key):
        """ _sqlJoin(self, j_tbl, r_tbl, j_key, r_key) --> str\n 

        Returns the join query.
        """

        s= " JOIN %s on " % r_tbl
        s+=_A(\
            [" %s.%s = %s.%s " % (r_tbl,v,j_tbl,k)
                for k,v in j_key.iteritems()])
        s+=" WHERE "
        s+=_A(\
            ["%s.%s = %%(%s)s " % (r_tbl,v,k)\
                for k,v in r_key.iteritems()])

        return s

    def _sqlJoinParent(self, builder, j_builder, outer=False):
        """ _sqlJoinParent(self, builder, j_builder [, outer= False]) --> (string, string)

        Returns a filter and the table where filter has to be applied.
        """

        tbl= builder.table_name
        j_tbl= j_builder.table_name

        if not self._isChildOf(j_tbl, tbl):

            try:
                j_tbl= self._searchRelation([tbl, j_tbl])
            except:
                self.log_int_raise('_sqlJoinParent: no way to join |%s| with |%s|.',
                                    ERR_COL(j_tbl), ERR_COL(tbl))

        m_d= self.getChildDeps(j_tbl, builder)

        if outer:
            s=" LEFT OUTER JOIN %s on " % j_tbl
        else:
            s=" JOIN %s on " % j_tbl

        s+=_A([" %(tbl)s.%(k)s = %(j_tbl)s.%(v)s " % locals() for k,v in m_d.iteritems()])

        return s, j_tbl

    def _getRelationObject(self, builders):
        """ _getRelationObject(builders): --> MotherFly

        builders is a list of table names.
        """
        
        rel_tbl_name= self._searchRelation(builders+ [self.table_name])

        # Return a builder with MotherOnTheFly.
        rel_obj= getMotherObj(rel_tbl_name, session= self.session)
        return rel_obj

    def getChildDeps(self, tbl_name, father= None):
        """ getChildDeps(self, tbl_name [, father= None]) --> dict

        Wich fields in TABLE tbl_name will be inherited from father?
        Returned dict is the mapping_dict.
        If father is None, self is assumed.
        """

        father=father or self
        tbl= father.table_name

        # Load father children.....
        d= self._table_children(tbl)

        # Get the dependencies for child tbl_name
        try:
            dd= d[tbl_name].copy()
        except:
            self.log_int_raise(\
                "Child %s not specified for table %s.",\
                    ERR_COL(tbl_name),ERR_COL(tbl))

        return dd

    def _getDepDict(self, mapping_dict, father= None):
        """_getDepDict(mapping_dict [,father= None]): ---> dict

        Given the dependencies mapping_dict, a new dict is created,
        filled and returned with father values.
        If father is None, self is assumed.
        """

        father= father or self
        new_d={}

        self.log_insane( 
                "Exporting values %s ",
                str(mapping_dict))
                        
        fkeys= set(mapping_dict.keys())

        try:
            d= father.getFields(fkeys)
        except Exception, s:
            self.log_int_raise("Cannot inherit needed values from father: %s.",
                    ERR_COL(list(fkeys)))
            
        for mapkey in mapping_dict:
            mapped_field= mapping_dict[mapkey]
            new_d[mapped_field]= d[mapkey]

        return new_d

    def _fillChildDict(self, child_obj, father= None):
        """_fillChildDict(child_obj [,father= None]) --> None
        Given a child object, his store is updatetd with
        father dependencies. If father is None, father= self.
        """
        
        father= father or self
        ct= child_obj.table_name
        
        # Ge dependencies ...
        d= self.getChildDeps(ct,father)
        # Create new dict ...
        new_d= self._getDepDict(d,father)
        # Init child with new_dict
        child_obj.setFields(new_d, safe_mode= False)

        self.log_insane( 
                "Child object(%s) updated with values %s.",\
                OKI_COL(ct),OKI_COL(new_d))

    def _workChild(self, child_builder, child_dict, flag):
    
        # Build obj...
        new_obj= child_builder(child_dict, session= self.session)
            # Inherit father fields...
        self._fillChildDict(new_obj)
                # Get properly function...
        f= new_obj._flag_actions(new_obj, flag)
                    # Get some debugging vars
        tbl= new_obj.table_name
        sd= str(new_obj.getFields())
        s= self._mo_flags[flag]
                # Execute function....
        f()
            # Debug
        self.log_insane( 
                "%s child %s, values %s",
                s,tbl,sd)

        return new_obj
        # Return ;)

      
#
##
### Public
##
#

    def getChild(self, cbuilder, c_dict):
        """ getChild(cbuilder, c_dict): --> MotherObj
        """
        return self._workChild( 
                                cbuilder,
                                c_dict,
                                MO_LOAD,
                              )
                                    

    def insertChild(self, cbuilder, c_dict):
        """ insertChild(child_builder, c_dict) -> MotherObj
        insert new child object and return it.
        """
        return self._workChild( 
                                cbuilder,
                                c_dict,
                                MO_SAVE,
                              )

    def updateChild(self,cbuilder,c_dict):
        """ updateChild(cbuilder, c_dict): --> MotherObj
        """
        return self._workChild( 
                                cbuilder,
                                c_dict,
                                MO_UP,
                              )
        
    def deleteChild(self,cbuilder,c_dict):
        """ deleteChild(cbuilder, c_dict): --> MotherObj
        """
        return self._workChild( 
                                cbuilder,
                                c_dict,
                                MO_DEL,
                              )

    def deleteChildren(self, cbuilder, filter= None):
        """ deleteChildren(cbuilder [,filter=None]) --> None
        
        Delete a set of children, filtered with filter.
        If filter is None, delete all children.
        cbuilder is a Mother class for the table where records
        wil be deleted.
        """
        child_table= cbuilder.table_name
        d= self.getChildDeps(child_table)
        d= self._getDepDict(d)

        qry_filter= self._sqlBuildFilter(d, tbl=child_table)
        if filter is not None:
            qry_filter+= " AND %s" % \
                    self._sqlBuildFilter(filter, tbl=child_table)

        MotherBox(cbuilder, qry_filter, MO_DEL)

    def updateChildren(self, cbuilder, d_up, filter= None):
        """ updateChildren(self, cbuilder, d_up [, filter= None]) --> None

        Update children with dict d_up.
        If filter, update only filtered children.
        cbuilder is a Mother class for the table.
        """
        child_table= cbuilder.table_name
        d= self.getChildDeps(child_table)
        d= self._getDepDict(d)

        # Build filter
        qry_filter= self._sqlBuildFilter(d, tbl=child_table)
        if filter is not None:
            qry_filter+= " AND %s" % \
                    self._sqlBuildFilter(filter, tbl=child_table)

        MotherBox(cbuilder, flag=MO_UP, fields= d_up, 
                filter= qry_filter, session= self.session) 

    def getChildren(self, cbuilder, fields= None, filter= None,\
                    jbuilder= None, jfilter= None,
                    outer= False, order= None):
        " getChildren(cbuilder [,fields= None, filter= None, "\
        " jbuilder= None, jfilter= None, outer= False, order= None])-->box\n" \
        """
        fields = list of fields names
        filter = string or dictionary
        jbuilder = builder class which will be joined
        jfilter = string or dictionary about join table fields
        outer = boolean, will be a left outer join
        order = list(fields)
        """

        child_table= cbuilder.table_name
        # Get dependencies dict...
        d= self.getChildDeps(child_table)
        d= self._getDepDict(d)

        # If a join filter is specified, we need
        # to build the JOIN STATEMENT before the WHERE
        qry_filter, joining_table= jbuilder and \
            self._sqlJoinParent(cbuilder, jbuilder, outer) or \
                ("", "")

        # Build SQL filter string
        qry_filter+= " WHERE "

        # now filter father dependencies
        qry_filter+= self._sqlBuildFilter(d, tbl= child_table)

        # Filter join condition
        # Note that the filter is used on the joined table
        # that is returned by _sqlJoinParent
        if jbuilder and jfilter:
            qry_filter+=" AND %s " %\
                self._sqlBuildFilter(\
                jfilter, tbl= joining_table)

        # if filter is specified, use it ...
        # the filter `filter` is used on Child Table.
        if filter is not None:
            qry_filter+=" AND  %s " %\
                self._sqlBuildFilter(filter,tbl=child_table)

        # Let's use a MotherBox :)
        # If join use DISTINCT
        distinct= (jbuilder is not None)
        mb= MotherBox(cbuilder, filter= qry_filter, flag= MO_LOAD,
                        fields= fields, order= order, distinct= distinct, 
                        session= self.session)
        # Oh yes, return ;)
        return mb

    def assignRelation_nt(self, builder_list, params= {}):
        """ """

        # Handle given objects ...
        builders= []
        fathers= []
        self.log_insane("Creating auto-relation...")

        for builder, d, flag in builder_list:
            tbl= builder.table_name
            builders.append(tbl)
            flag_str= self._mo_flags[flag]
            self.log_insane(
                    "Working record in %s with "\
                    "values %s and flag=%s...",\
                    INF_COL(tbl),INF_COL(str(d)),flag_str)
            try:
                new_obj= builder(d, flag, session= self.session)
                fathers.append(new_obj)
            except Exception,s:
                es=("Error! %s initialization returned: %s."\
                              " Now Rollback!" % (flag_str,ERR_COL(str(s))))
                self.log_error(es)
                self.log_int_raise("Unable to assign child: %s",es)

        self.log_insane( 
                "%s child(ren) assigned, now work on "\
                "relation...", OKI_COL(len(builders)))

        # create auto relation ...
        # init child object 
            
        # self.table_name is added automatically
        r_obj = self._getRelationObject(builders)

        # Let child take his values from fathers...
        for father in [self]+fathers:
            self._fillChildDict(r_obj, father)
        r_obj.setFields(params, safe_mode=False)

        # store it to DB
        try:
            r_obj.insert()
        except Exception,s:
            self.log_error("Error! insert() returned: %s."\
                                " Now Rollback!",ERR_COL(str(s)))
            self.log_int_raise("Error adding auto-relation.") 

        # All was fine.
        self.log_insane( 
                "Relation %s auto-saved with "\
                "values %s.",r_obj.table_name,\
                OKI_COL(str(r_obj._store)))

        return tuple(fathers)

    def assignRelation(self, builder_list, params= {}):
        """ assignRelation(self, builder_list [,params= {}]) --> Tuple

        assign a Relation.
        builder_list is a list of tuples:

            [(MotherBuilder,dict,flag)...]

        If TABLE A and TABLE B are related with the relation TABLE
        R_AB, you can (pseudo-code):

            me= Mother(A)
            builder_list=[(B, dict, MO_SAVE)]
            me.assignRelation(builder_list)

        With this call, create a record in B and put in R_AB the 
        relation.
        If the relation accept other fields, specify them in params,
        which is a dict.
        """

        self.beginTrans()
        try:
            res= self.assignRelation_nt(builder_list, params)
        except Exception,s:
            self.rollback()
            raise Exception(s)

        self.commit()
        return res
            
    def dropRelation(self, builder_list, params= {}):
        """ dropRelation(self, builder_list [,params= {}]) --> list

        Drop relation between father in builder_list.
        builder_list is [(builder,dict,flag),...]
        """
        fathers=[]
        fathers_del=[]
        # Create the list of only builders
        for builder, d, flag in builder_list:
            new_obj= builder(d, session= self.session)
            fathers.append(new_obj)
            if flag==MO_DEL:
                fathers_del.append(new_obj)

        # create auto relation init child object
        t_names=[o.table_name for o in fathers]
        r_obj = self._getRelationObject(t_names)
        
        # Let child take his values from fathers...
        for father in [self]+fathers:
            self._fillChildDict(r_obj,father)

        r_obj.setFields(params)
        r_obj.delete()
        self.log_insane("Dropped relation %s",\
                OKI_COL(r_obj.table_name))
        # Destroy all fathers with flag MO_DEL
        for f in fathers_del:
            f.delete()
        return fathers
    
    def dropRelations(self, builder, filter= None, jfilter= None, r_tbl= None, flag= MO_NOA):
        """ dropRelation(builder [,filter=None, r_tbl= None, flag= MO_NOA]):

        drop all related records.
        If r_tbl is specified, use that table to join records,
        otherwise, r_tbl is deduced.

        fIlter is.. a filter. It applies to the relation table.
        jfIlter is.. a filter. It applies to the related table.
        """

        mbox= self.joinRelation(builder, filter= filter, jfilter= jfilter, r_tbl= r_tbl)

        self.log_insane( 
                "%s is dropping %s relation(s) ...",
                INF_COL(self.table_name), INF_COL(len(mbox)))

        ds= mbox.getRecords()
        for d in ds:
            self.dropRelation([(builder, d, flag)])

    def joinRelation(self, builder, fields= [], filter= None, jfilter= None, r_tbl= None, order= None):
        """ joinRelation(self, builder, fields= [], filter= None, jfilter= None, r_tbl= None, order= None) -> Box

        Get related data.
        Builder is the MotherBuilder for data to be searched.

        filter applies to the relation table.
        jfilter applies to the related table.

        r_tbl is the relation table used for the join. If not given,
        r_tbl is obtained automaically.
        if order is not None, the query will be ordered by fields in that list.
        """

        # Useful vars
        s_tbl= self.table_name
        b_tbl= builder.table_name

        # Try to get relation table....
        # If there is ambiguity, CRASH!
        if r_tbl is None:
            r_tbl= self._searchRelation([s_tbl, b_tbl])

        # Ok, the relation table is found
        # Getting the mapping child dicts
        s_d = self.getChildDeps(r_tbl)
        b_d = self.getChildDeps(r_tbl, builder)
        
        # If fields is not specified, fields for the
        # serached table are retrieved 
        if fields== []:
            fields= self._table_fields(b_tbl)

        self.log_insane("Joining %s and %s with relation %s "\
                        "and fields %s ...",s_tbl,b_tbl,\
                        INF_COL(r_tbl),INF_COL(str(fields)))
                        
        # Make the base filter query ...
        qry_filter = self._sqlJoin(b_tbl, r_tbl, b_d, s_d)

        # If filters are given, apply it.
        if filter:
            qry_filter+=" AND %s " % self._sqlBuildFilter(filter, tbl= r_tbl)
        if jfilter:
            qry_filter+=" AND %s " % self._sqlBuildFilter(jfilter, tbl= b_tbl)
        # Mogrify the obtained filter query ...
        qry_filter= self._sqlMogrify(qry_filter)

        # get a MotherBox
        mb= MotherBox(builder, qry_filter, MO_LOAD, fields, order, session= self.session)
        return mb

    def relParams(self, related, fields= None):
        """ relParams(self, related, fields) -> Mother Object

        Returns a Mother Object representing the relation record
        between `self' and related records.
        `related' is a list of related records (Mother Objects).
        if `fields' is not None, only these fields will be loaded."""

        btbls= [b.table_name for b in related]

        # self.table_name is added automatically
        r_obj = self._getRelationObject(btbls)

        # Let child take his values from fathers...
        for father in [self] + related:
            self._fillChildDict(r_obj, father)

        r_obj.load(fields= fields)

        return r_obj

    def initChildManager(self, children):
        """initChildManager(self,children) --> None
        
        You can define a Mother class for each Child-TABLE.
        In fact, children is a list of Mother Builders.
        The following functions will be wrappered:
            
            insertChild(Foo,dict)
            deleteChildren(Foo, filter)
            updateChildren(Foo, dict, filter)
            getChildren(builder,fields,filter,jbuilder,jfilter,outer,order)


        So, if FOO is a child, you can create the Mother Class
        Foo and have the methods:

            insertFoo(dict)
            deleteMultipleFoo(filter)
            updateMultipleFoo(dict, filter)
            getMultipleFoo(fields, filter, jbuilder, jfilter, outer, order)

        * NOTE: methods already defined are not overwritten.
        """

        def insert_child(builder):
            def fly_insert_child(d):
                return self.insertChild(builder, d)
            return fly_insert_child

        def delete_children(builder):
            def fly_delete_children(filter= None):
                return self.deleteChildren(builder, filter)
            return fly_delete_children

        def get_children(builder):
            def fly_get_children(fields= None, filter= None, 
                    jbuilder= None, jfilter= None, 
                    outer= False, order= None):
                return self.getChildren(builder, fields, 
                        filter, jbuilder, jfilter, 
                        outer, order)
            return fly_get_children
        
        def update_children(builder):
            def fly_update_children(d_up, filter= None):
                return self.updateChildren(builder, d_up, filter)
            return fly_update_children


        camel= DbMother.MotherNaming

        for c in children:
            ctbl= c.table_name

            attr_name= camel(ctbl, 'insert')
            if not hasattr(self, attr_name): 

                attr= insert_child(c)
                attr.__doc__=\
                        " %s(dict) --> child\n\n"                                       \
                        "insert a child. dict contains the fields values.\n"            \
                        "Don't specify the foreign key: it's assigned automatically.\n" \
                            % attr_name

                setattr(self, attr_name, attr)

            attr_name= camel(ctbl, 'deleteMultiple')
            if not hasattr(self, attr_name): 
                
                attr= delete_children(c)
                attr.__doc__=\
                        " %s(filter= None) --> None\n\n"                                \
                        "delete children. if filter is None, delete ALL children.\n"    \
                        "Don't specify the foreign key: it's assigned automatically.\n" \
                            % attr_name

                setattr(self, attr_name, attr)

            attr_name= camel(ctbl, 'getMultiple')
            if not hasattr(self, attr_name): 
                
                attr= get_children(c)
                attr.__doc__=\
                        " %s([fields= None, filter= None, jbuilder= None, "                     \
                        "jfilter, outer= None, order= None) --> MotherBox\n\n"                  \
                        "retrieve a set of children. filter is used for the children table.\n"  \
                        "jbuilder and jfilter are used to do advanced filtering.\n"             \
                        "If outer is True, an outer join is performed.\n"                       \
                        "order is a list of fields names.\n"                                    \
                        "Don't specify the foreign key: it's assigned automatically.\n"         \
                        % attr_name

                setattr(self, attr_name, attr)

            attr_name= camel(ctbl, 'updateMultiple')
            if not hasattr(self, attr_name): 
                
                attr= update_children(c)
                attr.__doc__=\
                        " %s(d_up, filter= None) --> None\n\n"                                      \
                        "update children. the dict d_up contains the fields (and their values)\n"   \
                        "to be updated. The filter is a usual filter.\n"                            \
                        "Don't specify the foreign key: it's assigned automatically.\n"             \
                        % attr_name

                setattr(self, attr_name, attr)

    def initRelationManager(self, rels):
        """initRelationManager(children) --> None
        
        This is a magic function.
        It's a wrapper for the methods:

            - assigRelation
            - joinRelation
            - dropRelation
            - dropRelations
            - relParams

        The wrappers will be:

            - assigFoo
            - joinFoo
            - dropFoo
            - dropMultipleFoo
            - paramsFoo

        NOTE: methods already defined are not overwritten.
        """

        def assign_rel(builder):
            def fly_assign_rel(d, flag= MO_NOA, params= {}):
                return self.assignRelation([(builder, d, flag)], params)[0]
            return fly_assign_rel

        def drop_rel(builder):
            def fly_drop_rel(d, flag= MO_NOA, params= {}):
                return self.dropRelation((builder, d, flag), params)[0]
            return fly_drop_rel

        def drop_rels(builder):
            def fly_drop_rels(filter= None, jfilter= None, r_tbl= None, flag= MO_NOA):
                return self.dropRelations(builder, filter, jfilter, r_tbl, flag)
            return fly_drop_rels

        def join_rels(builder):
            def fly_join_rels(fields= [], filter= None, jfilter= None, r_tbl= None, order= None):
                return self.joinRelation(builder, fields, filter, jfilter, r_tbl, order)
            return fly_join_rels

        def rel_params(builder):
            def fly_rel_params(d, fields= None):
                if not isinstance(d, DbMother):
                    d= builder(d)
                return self.relParams([d], fields)
            return fly_rel_params


        camel= DbMother.MotherNaming
        stbl= self.table_name

        for rel in rels:
            rtbl= rel.table_name
            rel_tbl= self._searchRelation([rtbl, stbl])

            attr_name= camel(rtbl, 'assign')
            if not hasattr(self, attr_name):
                attr= assign_rel(rel)
                attr.__doc__=\
                    " %s(dict[,flag= MO_NOA, params= {}]) --> DbMother instance\n\n"    \
                    "Handle the Mother object with dict and flag on the table |%s|,\n"  \
                    "and, after, insert a record on the relation table |%s|.\n"         \
                    "Don't specify the foreign key: it's assigned automatically.\n"     \
                    "Return the Mother Object."                                         \
                     % (attr_name, rtbl, rel_tbl)

                setattr(self, attr_name, attr)

            attr_name= camel(rtbl, 'dropMultiple')
            if not hasattr(self, attr_name):
                attr= drop_rels(rel)
                attr.__doc__=\
                    " %s([filter, r_tbl, flag]) --> None\n\n"                           \
                    "Drop a set of relations.\n"                                        \
                    "If flag is specified, do also this action on related records.\n"   \
                    "if r_tbl is specified, use that table to make the join, \n"        \
                    "otherwise use the table |%s|.\n"                                   \
                    "Don't specify the foreign key: it's assigned automatically.\n"     \
                     % (attr_name, rel_tbl)

                setattr(self, attr_name, attr)

            attr_name= camel(rtbl, 'join')
            if not hasattr(self, attr_name):
                attr= join_rels(rel)
                attr.__doc__=\
                    " %s([fields, filter, jfilter, r_tbl, order]) --> MotherBox\n\n"          \
                    "Do a join with records in |%s|.\n"                                       \
                    "If filter is specified, apply it on the relation table.\n"               \
                    "If jfilter is specified, apply it on the related table.\n"               \
                    "If r_tbl is specified, use that as relation table to make the join,\n"   \
                    "otherwise the table |%s| will be used.\n"                                \
                    "If fields is specified, load only these fields from the joined table.\n" \
                    "Don't specify the foreign key: it's assigned automatically.\n"           \
                    "Return a MotherBox object."                                              \
                     % (attr_name, rtbl, rel_tbl)

                setattr(self, attr_name, attr)

            attr_name= camel(rtbl, 'params')
            if not hasattr(self, attr_name):
                attr= rel_params(rel)
                attr.__doc__=\
                    " %s(obj [,fields= None]) --> Mother Object\n\n"                \
                    "Returns a Mother Object that represents the relation\n"        \
                    "between `self' and the record on the table |%s|, which is\n"   \
                    "represented by obj (a dict or a Mother Object).\n"             \
                    "If `fields' is specified, load only these fields."             \
                    % (attr_name, rtbl)

                setattr(self, attr_name, attr)



#
##
### The MotherBox
##
#

class MotherBox(Psygres):
    def __init__(self, builder, filter= None, flag= MO_NOA, 
                    fields= None, order= None, session= None, 
                    distinct= False, notriggers= False):

        if session:
            session._import(self)

        self.session= session
        self.builder= builder
        self.momma= builder()
        self.notriggers= notriggers
    
        if filter:
            filter= self.momma._sqlBuildFilter(filter, True)
        else:
            filter= ''

        self._store= []

        if flag==MO_LOAD:
            self.loadBox(filter, fields, order, distinct)

        elif flag==MO_DEL:
            self.deleteBox(filter)

        elif flag==MO_UP:
            if not fields:
                self.log_int_raise(
                        "No fields specified to update %s.", 
                        ERR_COL(self.builder.table_name))
            self.updateBox(filter, fields)

        elif flag== MO_NOA:
            return

        else:
            self.log_int_raise("Invalid Flag %s for MotherBox", ERR_COL(flag))

        del self.momma


    def __len__(self):
        return len(self._store)

    def _retrieve_mothers(self, filter):

        where= filter or ''
        tbl= self.momma.table_name
        fields= self.momma.pkeys
        fields= _J([f for f in fields])

        qry= "SELECT %(fields)s from %(tbl)s %(where)s" % locals()

        res= self.mr_query(qry)
        session= self.session
        mothers= [self.builder(d, session= session) for d in res]

    def _trigger_mothers(self, mothers, flag, when):
        for m in mothers:
            m.trigger(flag, when)

    def _loadBox(self, filter, fields, order, distinct):

        tbl= self.builder.table_name

        self.log_insane(
                "Mbox: loading with filter=%s",\
                INF_COL(filter))

        distinct= distinct and 'DISTINCT' or ''

        sel="SELECT %(distinct)s %(what)s FROM %(tbl)s %(filter)s "

        if filter:
            filter= self.momma._sqlBuildFilter(filter, prefix= True)
        else:
            filter= ''

        if fields is None:
            what= "%s.*" % tbl
        else:
            ifields= set(fields) - self.momma.fields
            if len(ifields):
                self.log_int_raise("Invalid Fields for table %s: %s",
                    tbl, ERR_COL(list(ifields)))
            what= _J(['%s.%s' % (tbl, f) for f in fields])

        qry= sel % locals()

        if order:
            qry+= " ORDER BY %s" % _J(order)

        self._store= self.mr_query(qry)

        self.log_debug("Loaded %s records on %s",\
                OKI_COL(len(self._store)), tbl)

        self.log_info("Action %s.", OKI_COL("completed"))


    def loadBox(self, filter, fields, order, distinct):

        if self.notriggers or not self.momma.has_trigger(MO_LOAD):
            self._loadBox(filter, fields, order, distinct)
            return 
            
        self.log_info("Launching Triggers on Box (MO_LOAD)...")
        ms= self._retrieve_mothers(filter)
        self._trigger_mothers(ms, MO_LOAD, MO_BEFORE)
        self._loadBox(filter, fields, order, distinct)
        self._trigger_mothers(ms, MO_LOAD, MO_AFTER)

    def updateBox(self, d, filter= None):

        if self.notriggers or not self.momma.has_trigger(MO_UP):
            self._updateBox(d, filter)
            return

        self.log_info("Launching Triggers on Box (MO_UP) operation...")
        ms= self._retrieve_mothers(filter)
        self._trigger_mothers(ms, MO_UP, MO_BEFORE)
        self._updateBox(d, filter)
        self._trigger_mothers(ms, MO_UP, MO_AFTER)

    def _updateBox(self, filter, d):
        """ updateBox(self, filter, d): --> None

        Update a set of records.
        """

        table= self.builder.table_name

        if filter:
            ftr= self.momma._sqlBuildFilter(filter, prefix= True)
        else:
            ftr= ''

        res= []
        for k, v in d.iteritems():
            if v== SQL_DEFAULT:
                res.append('%s= DEFAULT' % k)
            else:
                res.append('%s= %%(%s)s' % (k, k))

        what= _J(res)
        tbl= self.builder.table_name

        qry= 'UPDATE %(tbl)s SET %(what)s %(ftr)s' % locals()
        qry= self.mogrify(qry, d)

        self.oc_query(qry)
        self.log_insane("Box: Updatetd records.")

        self.log_info("Action %s.", OKI_COL("completed"))

    def deleteBox(self, filter=None):

        if self.notriggers or not self.momma.has_trigger(MO_DEL):
            self._deleteBox(filter)
            return 
        
        self.log_info("Launching Triggers on Box (MO_DEL) operations...")
        ms= self._retrieve_mothers(filter) 
        self._trigger_mothers(ms, MO_DEL, MO_BEFORE)
        self._deleteBox(filter)
        self._trigger_mothers(ms, MO_DEL, MO_AFTER)

    def _deleteBox(self, filter=None):
        """ deleteBox(self, filter= None, stored= False) --> None

        delete from DB.
        If stored is True, delete stored object.
        """

        self.log_insane("Mbox: deleting with filter=%s", INF_COL(filter))

        tbl= self.builder.table_name
        if filter:
            filter= self.momma._sqlBuildFilter(filter, prefix= True)
        else:
            filter= ''

        qry="DELETE FROM %(tbl)s %(filter)s" % locals()
        self.oc_query(qry)
        self.log_insane("Records deleted.")

        self.log_info("Action %s.", OKI_COL("completed"))

    def getRecords(self, flag_obj= False):
        """ getRecords(self, flag_obj= False) -> list(dict) or list(Mothers)

        The return value depends on flag_obj.
        """
        if flag_obj:
            b= self.builder
            session= self.session
            return [b(d, MO_NOA, session) for d in self._store]
        else:
            return self._store

    def getFields(self, flag_obj= False):
        """ getRecords(self, flag_obj= False) -> list(dict) or list(Mothers)

        The return value depends on flag_obj.
        MotherBox.getFields is obsolete! Use MotherBox.getRecords!
        """
        self.log_warning(ERR_COL(
            "Obsolete Method MotherBox.getFields(). Use getRecords() instead! "
            "This method will be removed from Mother."))
        return self.getRecords(flag_obj)
