package org.jcon.df.request;

import org.jcon.util.DataLib;
import org.jcon.df.Entity;
import org.jcon.df.EntityChild;
import org.jcon.df.Schema;
import org.jcon.df.work.Row;
import java.util.Enumeration;
import java.util.Vector;

/**
 * Performs cascade deletes plus associated tasks, such as
 * providing needed entityLockNames. All static methods to
 * allow fast use by multiple threads or instances.
 *
 * @author Jack Harich
 */
public class CascadeDelete {

//---------- Public Methods ------------------------------
/**
 * Returns an array of the entity names that should be
 * locked during the transaction. Same purpose as the same
 * method in Integrity.
 */
public static String[] getEntityLockNames(RemoveRows request,
        Schema schema) {

    String entityName = request.getPrimeEntity();
    Entity entity = schema.getEntity(entityName);

    if (! entity.hasChildren()) {
        // Only lock this entity
print(".getEntityLockNames() - No children, entityName = " + entityName);
        return new String[] { entityName };
    } else {
        // Has children
print(".getEntityLockNames() - Has children");
        Vector names = new Vector();
        loadEntityLockNames(entity, names, schema);
        return DataLib.convertVectorToStringArray(names);
    }
}
/**
 * Performs a cascade delete using the arguments.
 * This is dependent on all entities having correctly
 * defined EntityChild relationships. Returns the number of
 * rows deleted in the prime entity.
 * <p>
 * *** Need to wrap in transaction, here or performer ***
 */
public static int performDelete(RemoveRows request,
        RequestServices services) {

    Schema schema = services.getSchema();
    String entityName = request.getPrimeEntity();
    Entity entity = schema.getEntity(entityName);

    if (! entity.hasChildren()) {
        // Only delete rows in this entity, the usual case
        return deleteRows(entityName, request.getFilter(), services);
    } else {
        // Has children
        return deleteParent(entityName,
            request.getFilter(), services);
    }
}
//---------- Private Methods -----------------------------
// RECURSIVE - Stops when no children
private static void loadEntityLockNames(Entity entity,
        Vector names, Schema schema) {

    names.addElement(entity.getName());

    // The following ends recursion if no children
    Enumeration enum = entity.getChildren();
    while (enum.hasMoreElements()) {
        EntityChild child = (EntityChild)enum.nextElement();
        String childEntityName = child.getEntityName();
        Entity childEntity = schema.getEntity(childEntityName);
        // Recurse
        loadEntityLockNames(childEntity, names, schema);
    }
}
// RECURSIVE - Deletes the parent and its children
// Stops when no children
private static int deleteParent(String entityName,
        Filter filter, RequestServices services) {

    //print(".deleteParent() - entityName = " + entityName);
    String columnID = entityName + ".MID";

    //----- Get parent rows
    ReadRowSet reader = new ReadRowSet();
    reader.addEntity(entityName);
    reader.addColumnID(columnID);
    reader.setFilter(filter);
    reader.setRequestServices(services);
    reader.perform();

    //----- For each row delete that branch
    while (reader.hasMoreRows()) {
        Row row = reader.nextRow();
        String mid = (String)row.getValue(columnID);
        deleteChildren(entityName, mid, services);
    }
    //----- Delete parent
    return deleteRows(entityName, filter, services);
}
// Part of deleteParent() recursive call
private static void deleteChildren(String entityName,
        String mid, RequestServices services) {

    Entity entity = services.getSchema().getEntity(entityName);
    Enumeration enum = entity.getChildren();
    while (enum.hasMoreElements()) {
        EntityChild child = (EntityChild)enum.nextElement();
        String childEntityName = child.getEntityName();
        //print(".deleteChildren() - childEntityName = " + childEntityName);
        Entity childEntity = services.getSchema().getEntity(childEntityName);
        // Prepare filter
        Filter filter = new Filter();
        filter.addExpression(child.getColumnID(), "=", mid);
        // RECURSE
        deleteParent(childEntityName, filter, services);
        // Delete rows after recurse
        deleteRows(childEntityName, filter, services);
    }
}
private static int deleteRows(String entityName,
        Filter filter, RequestServices services) {

    // Prepare request
    RemoveRowsNoCascade request = new RemoveRowsNoCascade();
    request.addEntity(entityName);
    request.setFilter(filter);
    request.setRequestServices(services);
    request.prepare();

    // Perform request
    request.perform();
    return request.getNumberRowsDeleted();
}
//--- Std
private static void print(String text) {
    System.out.println("CascadeDelete" + text);
}

} // End class
