package org.jcon.df.work;

import org.jcon.util.GenLib;
import org.jcon.df.Database;
import org.jcon.df.request.*;

/**
 * Contains database utilities that allow a more concise
 * request implementation.
 *
 * @author Jack Harich
 */
public class DatabaseUtil {

//---------- Private Fields ------------------------------
private Database        database;
private RequestServices services;

//---------- Initialization ------------------------------
/**
 * Creates an instance that will use the database to perform
 * utility calls. This is for use on the client. These
 * calls are validated.
 */
public DatabaseUtil(Database database) {
    if (database == null) {
        throw new IllegalArgumentException("Argument 'database' cannot be null.");
    } else {
        this.database = database;
    }
}
/**
 * Creates an instance that will use RequestServices to perform
 * utility calls. This is for use on the server or for
 * internal data framework logic. These calls are not
 * validated. To allow changing the RequestServices for
 * calls that property MUST be provided with
 * setRequestServices().
 */
public DatabaseUtil() {
}
//---------- Properties ----------------------------------
public void setRequestServices(RequestServices services) {
    this.services = services;
}
//---------- Public Methods ------------------------------
/**
 * Reads and returns a Row in entityName using the filter.
 * The first row found is returned. Returns null if none
 * found or unsuccessful.
 */
public Row readRow(String entityName, Filter filter) {

    ReadRow reader = new ReadRow();
    reader.addAllColumnIDs(entityName + ".*", database.getSchema());
    reader.addEntity(entityName);
    reader.setFilter(filter);

    reader = (ReadRow)perform(reader);
    if (reader.isSuccessful()) {
        return reader.getRow(); // null if none
    } else {
        reader.presentFailure();
        return null;
    }
}
/**
 * Creates and returns a RowSet from a single entity.
 * filter. Returns null if failure.
 */
/** public RowSet createEntityRowSet(String entityName,
        String[] columnIDs, Filter filter) {
    ReadRowSet readRowSet = new ReadRowSet();
    readRowSet.addEntity(entityName);
    readRowSet.setFilter(filter);

} * // ***** may not do
/**
 * Reads the columnName in entityName using the filter,
 * returning the column's value. Throws an
 * IllegalStateException if no matches.
 */
public String readColumn(String columnName,
        String entityName, Filter filter) {

    String columnID = entityName + "." + columnName;

    ReadRow reader = new ReadRow();
    reader.addColumnID(columnID);
    reader.addEntity(entityName);
    reader.setFilter(filter);

    reader = (ReadRow)perform(reader);
    Row row = reader.getRow();
    if (row == null) {
        throw new IllegalStateException("Row not found " +
        "using columnName '" + columnName + "' in entity '"
        + entityName + "'.");
    } else {
        return (String)row.getValue(columnID);
    }
}
/**
 * Adds the row to entityName. Returns true for success or
 * complains and returns false for failure. The row will
 * contain the new MID but NOT the TStamp (currently).
 * The row is validated.
 */
public boolean addRow(String entityName, Row row) {
    // Validate row
    print(".addRow() - before validate");
    InvalidRow invalidRow = database.getSchema()
        .getEntity(entityName).validateAddRow(row);
    if (invalidRow != null) {
        GenLib.helpfulHint(invalidRow.getText());
        return false; // <-----<<<
    }
    // Add row to database
    print(".addRow() - before add");
    AddOneRow request = new AddOneRow();
    request.setRow(row);
    request.setEntityName(entityName);

    request = (AddOneRow)database.performRequest(request);
    print(".addRow() - after add");

    // Use result to update MID
    if (request.isSuccessful()) {
        String newMID = request.getNewMID();
        row.setValue(entityName + ".MID", newMID);
        return true; // <-----<<<
    } else {
        request.presentFailure("DatabaseUtil.addRow()");
        return false; // <-----<<<
    }
}
/**
 * Returns true if entityName is in the database, false if
 * not.
 */
public boolean isEntityInDatabase(String entityName) {
    // *** Later do this with meta data
    CalcAggregate calc = new CalcAggregate();
    calc.addEntity(entityName);
    calc.setAggregateExpression("*");
    calc.setAggregateType("COUNT");
    // Do not show failure if unsuccessful
    calc = (CalcAggregate)perform(calc);
    return calc.isSuccessful();
}
/**
 * Returns the number of rows in entityName using the
 * optional filter, which builds the Where clause.
 * For no filter use null. Returns -1 if failure, including
 * table not in database.
 */
public int countRows(String entityName, Filter filter) {
    CalcAggregate calc = new CalcAggregate();
    calc.addEntity(entityName);
    calc.setFilter(filter);
    calc.setAggregateExpression("*");
    calc.setAggregateType("COUNT");

    calc = (CalcAggregate)perform(calc);
    if (calc.isSuccessful()) {
        //print(".countRows() - success for " + entityName);
        return calc.getAnswerAsInt();
    } else {
        //print(".countRows() - failure for " + entityName);
        calc.presentFailure();
        return -1;
    }
}
/**
 * Returns the aggregateType value of expression in
 * entityName using the optional filter. The aggregateType
 * may be COUNT, SUM, AVERAGE, MIN or MAX. The expression
 * is usually a column name. In the case of
 * COUNT the expression is usually "*". If a problem occurs
 * Double.NEGATIVE_INFINITY is returned.
 */
public double calcAggregate(String entityName,
        Filter filter, String aggregateType,
        String expression) {

    CalcAggregate calc = new CalcAggregate();
    calc.addEntity(entityName);
    calc.setFilter(filter);
    calc.setAggregateExpression(expression);
    calc.setAggregateType(aggregateType);

    calc = (CalcAggregate)perform(calc);
    return calc.getAnswerAsDouble();
}
/**
 * Removes rows in entityName using the filter. Returns
 * true for success, false for failure.
 */
public boolean removeRows(String entityName, Filter filter) {
    RemoveRows removeRows = new RemoveRows();
    removeRows.addEntity(entityName);
    removeRows.setFilter(filter);
    removeRows = (RemoveRows)perform(removeRows);
    return removeRows.isSuccessful();
}
/**
 * Removes entityName from the database. Returns true for
 * success, false for failure.
 */
public boolean removeTable(String entityName) {
    RemoveTable removeTable = new RemoveTable();
    removeTable.addEntity(entityName);
    removeTable = (RemoveTable)perform(removeTable);
    return removeTable.isSuccessful();
}
/**
 * Adds entityName to the database. Returns true for
 * success, false for failure, such as if already there.
 */
public boolean addTable(String entityName) {
    AddTable addTable = new AddTable();
    addTable.addEntity(entityName);
    addTable = (AddTable)perform(addTable);
    return addTable.isSuccessful();
}
//---------- Private Methods -----------------------------
private Object perform(DataRequest request) {
    if (database != null) {
        return database.performRequest(request);
    } else {
        request.setRequestServices(services);
            request.perform();
        request.setRequestServices(null);

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

} // End class
