package org.jcon.df.request;

import org.jcon.df.Connector;
import org.jcon.df.Integrity;
import org.jcon.df.Schema;
import org.jcon.util.GenLib;
import org.jcon.util.lock.LockPool;
import org.jcon.util.lock.LockSet;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;

/**
 * Contains methods which provide services the request may
 * use while performing its work.
 * <p>
 * This class is DELIBERATELY not Serializable. Clients
 * should store this class as transient, especially local
 * classes performing remote work.
 * <p>
 * Additional services will be added as necessary.
 *
 * @author Jack Harich
 */
public class RequestServices {

//---------- Private Fields ------------------------------
private Connector    connector;
private Translator   translator;
private Statement    statement;
private Integrity    integrity;
private Schema       schema;
private LockPool     entityLockPool;
private LockSet      transactionLockSet;

//---------- Properties ----------------------------------
//----- Various
public void setConnector(Connector connector) {
    this.connector = connector;
}
public Statement getStatement() {
    return statement;
}
public void setIntegrity(Integrity integrity) {
    this.integrity = integrity;
}
//----- schema
public void setSchema(Schema schema) {
    this.schema = schema;
}
public Schema getSchema() {
    return schema;
}
//----- translator
public void setTranslator(Translator translator) {
    this.translator = translator;
}
public Translator getTranslator() {
    return translator;
}
//----- entityLockPool
public void setEntityLockPool(LockPool entityLockPool) {
    this.entityLockPool = entityLockPool;
}
public LockPool getEntityLockPool() {
    return entityLockPool;
}
//---------- Public Methods ------------------------------
public String validateRequest(Request request) {
    return integrity.validateRequest(request, this);
}
public Connector cloneConnector() {
    return (Connector)connector.clone();
}
/**
 * Prepares this class's resources in anticipation of
 * servicing a request. Currently this is just creating
 * the statement and acquiring locks.
 */
public void prepareResources(Request request) {
// statement
if (statement == null) {
    try {
        Connection connection = connector.retrieveConnection();
        statement = connection.createStatement();
    } catch(SQLException ex) {
        GenLib.exception("RequestServices.prepareResources()",
            "Cannot create Connection or Statement.", ex);
    }
}
// transactionLockSet
if (request == null) return;

String[] entityNames = integrity.getEntityLockNames(request);
if (entityNames.length > 0) {
    transactionLockSet = entityLockPool.lockObjects(
        (Object[])entityNames);
}
} // End method
/**
 * Releases this class's resources after servicing a
 * request. Currently this is just closing the statement
 * and releasing locks.
 */
public void releaseResources() {
if (statement != null) {
    try {
        statement.close();
        statement = null;
    } catch(SQLException ex) {
        GenLib.exception("RequestServices.releaseResources()",
            "Cannot close Statement.", ex);
    }
}
if (transactionLockSet != null) {
    entityLockPool.unlock(transactionLockSet);
    transactionLockSet = null;
}
} // End method
/**
 * Creates and returns a new MID for entityName. This
 * should be called when the Statement provided to
 * the Request is not in use, since the same Statement
 * is used to create the MID.
 * <p>
 * The current internal scheme for issuing new MIDs is to
 * have unique MIDs per entity. The maximum MID for the
 * entity is looked up (in the database or memory),
 * incremented by one, and that value is the new MID.
 * <p>
 * This simple scheme allows bulk adds to pre-issue their
 * own MIDs and with a future easy modification to
 * AddOneRow, use their own MID.
 */

// *** Later optimize to retain lastMID in memory per entity
// Use separate class MIDFactory

public String createMID(String entityName) {
    // Calc maximum MID in entityName
    CalcAggregate calc = new CalcAggregate();
    calc.setRequestServices(this);
    calc.addEntity(entityName);
    calc.setAggregateExpression("MID");
    calc.setAggregateType("MAX");

    calc.perform();

    // Increment
    return String.valueOf(calc.getAnswerAsLong() + 1);
}
//---------- Private Methods -----------------------------
//--- Std
private static void print(String text) {
    System.out.println("RequestServices" + text);
}

} // End class
