'Delete all (deletable) records in this resource
@param format: the representation format of the request (optional)
@param cascade: this is a cascade delete (prevents rollbacks/commits)
@param replaced_by: used by record merger
@return: number of records deleted'

def delete(self, format=None, cascade=False, replaced_by=None):

    s3db = current.s3db
    self.error = None
    permission_error = False
    tablename = self.tablename
    table = self.table
    table_fields = table.fields
    get_config = self.get_config
    pkey = self._id.name
    fields = [pkey]
    add_field = fields.append
    supertables = get_config('super_entity')
    if supertables:
        if (not isinstance(supertables, (list, tuple))):
            supertables = [supertables]
        for sname in supertables:
            stable = (s3db.table(sname) if isinstance(sname, str) else sname)
            if (stable is None):
                continue
            key = stable._id.name
            if (key in table_fields):
                add_field(key)
    if ('uuid' in table_fields):
        add_field('uuid')
    rows = self.select(fields, as_rows=True)
    if (not rows):
        return 0
    first = rows[0]
    if (hasattr(first, tablename) and isinstance(first[tablename], Row)):
        joined = True
    else:
        joined = False
    numrows = 0
    db = current.db
    has_permission = current.auth.s3_has_permission
    audit = current.audit
    prefix = self.prefix
    name = self.name
    define_resource = s3db.resource
    delete_super = s3db.delete_super
    DELETED = current.xml.DELETED
    INTEGRITY_ERROR = current.ERROR.INTEGRITY_ERROR
    if (current.deployment_settings.get_security_archive_not_delete() and (DELETED in table)):
        if (not cascade):
            s3db.load_all_models()
        if db._lazy_tables:
            for tn in db._LAZY_TABLES.keys():
                db[tn]
        references = table._referenced_by
        try:
            rfields = [f for f in references if (f.ondelete == 'RESTRICT')]
        except AttributeError:
            references = [db[tn][fn] for (tn, fn) in references]
            rfields = [f for f in references if (f.ondelete == 'RESTRICT')]
        pkey_ = (str(self._id) if joined else pkey)
        deletable = set((row[pkey_] for row in rows))
        for rfield in rfields:
            if deletable:
                (fn, tn) = (rfield.name, rfield.tablename)
                rtable = db[tn]
                query = rfield.belongs(deletable)
                if (tn == self.tablename):
                    query &= (rfield != rtable._id)
                if (DELETED in rtable):
                    query &= (rtable[DELETED] != True)
                rrows = db(query).select(rfield)
                for rrow in rrows:
                    deletable.discard(rrow[fn])
        ondelete_cascade = get_config('ondelete_cascade')
        for row in rows:
            if joined:
                row = getattr(row, tablename)
            record_id = row[pkey]
            if (not has_permission('delete', table, record_id=record_id)):
                permission_error = True
                continue
            error = self.error
            self.error = None
            if ondelete_cascade:
                try:
                    callback(ondelete_cascade, row, tablename=tablename)
                except:
                    continue
                if (record_id not in deletable):
                    restricted = False
                    for rfield in rfields:
                        (fn, tn) = (rfield.name, rfield.tablename)
                        rtable = db[tn]
                        query = (rfield == record_id)
                        if (tn == self.tablename):
                            query &= (rfield != rtable._id)
                        if (DELETED in rtable):
                            query &= (rtable[DELETED] != True)
                        rrow = db(query).select(rfield, limitby=(0, 1)).first()
                        if rrow:
                            restricted = True
                            break
                    if (not restricted):
                        deletable.add(record_id)
            if (record_id not in deletable):
                self.error = INTEGRITY_ERROR
                continue
            for rfield in references:
                (fn, tn) = (rfield.name, rfield.tablename)
                rtable = db[tn]
                query = (rfield == record_id)
                if (tn == self.tablename):
                    query &= (rfield != rtable._id)
                if (rfield.ondelete == 'CASCADE'):
                    rresource = define_resource(tn, filter=query, unapproved=True)
                    rresource.delete(cascade=True)
                    if rresource.error:
                        self.error = rresource.error
                        break
                elif (rfield.ondelete == 'SET    NULL'):
                    try:
                        db(query).update(**{fn: None})
                    except:
                        self.error = INTEGRITY_ERROR
                        break
                elif (rfield.ondelete == 'SET    DEFAULT'):
                    try:
                        db(query).update(**{fn: rfield.default})
                    except:
                        self.error = INTEGRITY_ERROR
                        break
            if ((not self.error) and (not delete_super(table, row))):
                self.error = INTEGRITY_ERROR
            if self.error:
                if (not cascade):
                    db.rollback()
                continue
            else:
                linked = self.linked
                if (linked and self.autodelete and linked.autodelete):
                    rkey = linked.rkey
                    fkey = linked.fkey
                    if (rkey in table):
                        query = (table._id == record_id)
                        this = db(query).select(table._id, table[rkey], limitby=(0, 1)).first()
                        query = ((table._id != this[pkey]) & (table[rkey] == this[rkey]))
                        if (DELETED in table):
                            query &= (table[DELETED] != True)
                        remaining = db(query).select(table._id, limitby=(0, 1)).first()
                        if (not remaining):
                            linked_table = s3db.table(linked.tablename)
                            query = (linked_table[fkey] == this[rkey])
                            linked = define_resource(linked_table, filter=query, unapproved=True)
                            linked.delete(cascade=True)
                self.error = error
                error = None
                data = {'deleted': True}
                record = None
                if ('deleted_fk' in table_fields):
                    record = table[record_id]
                    fk = {}
                    for f in table_fields:
                        if ((record[f] is not None) and s3_has_foreign_key(table[f])):
                            fk[f] = record[f]
                            data[f] = None
                        else:
                            continue
                    if fk:
                        data['deleted_fk'] = json.dumps(fk)
                idstr = str(record_id)
                if (replaced_by and (idstr in replaced_by) and ('deleted_rb' in table_fields)):
                    data['deleted_rb'] = replaced_by[idstr]
                if (table._tablename == tablename):
                    dbset = db((table._id == record_id))
                else:
                    dbset = db((db[tablename]._id == record_id))
                dbset.update(**data)
                numrows += 1
                if (s3_get_last_record_id(tablename) == record_id):
                    s3_remove_last_record_id(tablename)
                audit('delete', prefix, name, record=record_id, representation=format)
                ondelete = get_config('ondelete')
                if ondelete:
                    callback(ondelete, row)
                if (not cascade):
                    db.commit()
    else:
        for row in rows:
            if joined:
                row = getattr(row, tablename)
            record_id = row[pkey]
            if (not has_permission('delete', table, record_id=record_id)):
                permission_error = True
                continue
            success = delete_super(table, row)
            if (not success):
                self.error = INTEGRITY_ERROR
                continue
            try:
                del table[record_id]
            except:
                self.error = INTEGRITY_ERROR
                continue
            else:
                numrows += 1
                if (s3_get_last_record_id(tablename) == record_id):
                    s3_remove_last_record_id(tablename)
                audit('delete', prefix, name, record=row[pkey], representation=format)
                ondelete = get_config('ondelete')
                if ondelete:
                    callback(ondelete, row)
                if (not cascade):
                    db.commit()
    if (numrows == 0):
        if (not deletable):
            self.error = INTEGRITY_ERROR
        elif permission_error:
            self.error = current.ERROR.NOT_PERMITTED
    return numrows
