package hirondelle.web4j.database;

import hirondelle.web4j.model.Id;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 Version of {@link Db} for use in a transaction. 
 
 <P>The {@link Db} class uses an internal connection to execute single operations. 
 If more than one operation needs to be performed as an atomic transaction, then 
 this class is used instead.
 
 <P>This class is identical to {@link Db}, except that it uses a {@link Connection} passed
 by the caller.
 
<h3><a name="Parameters"></a>SQL Parameters</h3>
 The parameters for SQL statements used by this class have the same behavior as defined by 
 the <a href='Db.html#Parameters'>Db class</a>.
*/
public final class DbTx {

  /**
   <tt>SELECT</tt> operation which returns a single Model Object. 
    
   @param aConnection single connection shared by all operations in the transaction.
   @param aClass class of the returned Model Object.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
  */
  public static <T> T fetch(Connection aConnection, Class<T> aClass, SqlId aSqlId, Object... aParams) throws DAOException {
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    ModelFromRow<T> builder = new ModelFromRow<T>(aClass);
    return fetcher.fetchObject(builder);
  }

  /**
   <tt>SELECT</tt> operation which returns a single 'building block' value such as <tt>Integer</tt>, <tt>BigDecimal</tt>, and so on.
  
   @param aConnection single connection shared by all operations in the transaction.
   @param aSupportedTargetClass class supported by the configured 
   implementation of {@link ConvertColumn}.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
  */
  public static <T> T fetchValue(Connection aConnection, Class<T> aSupportedTargetClass, SqlId aSqlId, Object... aParams) throws DAOException {
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    ModelBuilder<T> builder = new ValueFromRow<T>(aSupportedTargetClass); 
    return fetcher.fetchObject(builder);
  }
  
  /**
   <tt>SELECT</tt> operation which returns <tt>0..N</tt> Model Objects, one per row.  
    
   @param aConnection single connection shared by all operations in the transaction.
   @param aClass class of the returned Model Objects.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
   @return an unmodifiable {@link List} of Model Objects. The list may be empty.
  */
  public static <T> List<T> list(Connection aConnection, Class<T> aClass, SqlId aSqlId, Object... aParams) throws DAOException {
    List<T> result = new ArrayList<T>();
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    ModelBuilder<T> builder = new ModelFromRow<T>(aClass);
    fetcher.fetchObjects(builder, result);
    return Collections.unmodifiableList(result);
  }
  
  /**
   <tt>SELECT</tt> operation which returns a <tt>List</tt> of 'building block' values such 
   as <tt>Integer</tt>, <tt>BigDecimal</tt>, and so on.
  
   @param aConnection single connection shared by all operations in the transaction.
   @param aSupportedTargetClass class supported by the configured 
   implementation of {@link ConvertColumn}.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
   @return an unmodifiable {@link List} of building block objects. The list may be empty.
  */
  public static <T> List<T> listValues(Connection aConnection, Class<T> aSupportedTargetClass, SqlId aSqlId, Object... aParams) throws DAOException {
    List<T> result = new ArrayList<T>();
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    ModelBuilder<T> builder = new ValueFromRow<T>(aSupportedTargetClass); 
    fetcher.fetchObjects(builder, result);
    return Collections.unmodifiableList(result);
  }
  
  /**
   <tt>SELECT</tt> operation that returns a <tt>List</tt> of Model Objects "subsetted" to 
   a particular range of rows.
   
   <P>This method is intended for paging through long listings. When the underlying 
   <tt>SELECT</tt> returns many pages of items, the records can be "subsetted" by 
   calling this method.
   
   <P>See {@link hirondelle.web4j.ui.tag.Pager}.
   @param aConnection single connection shared by all operations in the transaction.
   @param aClass class of the returned Model Objects.
   @param aSqlId identifies the underlying SQL statement.
   @param aStartIndex 1-based index indentifying the first row to be returned.
   @param aPageSize number of records to be returned.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
   @return an unmodifiable {@link List} of Model Objects. The list may be empty.
  */
  public static <T> List<T> listRange(Connection aConnection, Class<T> aClass, SqlId aSqlId, Integer aStartIndex, Integer aPageSize, Object... aParams) throws DAOException  {
    List<T> result = new ArrayList<T>();
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    fetcher.limitRowsToRange(aStartIndex, aPageSize);
    ModelBuilder<T> builder = new ModelFromRow<T>(aClass); 
    fetcher.fetchObjects(builder, result);
    return Collections.unmodifiableList(result);
  }

  /**
   <tt>INSERT</tt>, <tt>UPDATE</tt>, or <tt>DELETE</tt> operations which take parameters.
    
   @param aConnection single connection shared by all operations in the transaction.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
   @return the number of records affected by this edit operation.
  */
  public static int edit(Connection aConnection, SqlId aSqlId, Object... aParams) throws DAOException, DuplicateException {
    SqlEditor change = SqlEditor.forTx(aSqlId, aConnection, aParams);
    return change.editDatabase();
  }
  
  /**
   <tt>INSERT</tt> operation which returns the database identifier of the added record.
  
   <P>This operation is not supported by all databases. See 
   {@link java.sql.Statement} for more information.
   
   @param aConnection single connection shared by all operations in the transaction.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> for the SQL statement.
  */
  public static Id add(Connection aConnection, SqlId aSqlId, Object... aParams) throws DAOException, DuplicateException  {
    SqlEditor add = SqlEditor.forTx(aSqlId, aConnection, aParams);
    return new Id(add.addRecord());
  }

  /**
   <tt>DELETE</tt> operation which takes parameters.
    
   @param aConnection single connection shared by all operations in the transaction.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams identifies the item to be deleted. Often 1 or more {@link Id} objects.
   @return the number of deleted records.
  */
  public static int delete(Connection aConnection, SqlId aSqlId, Object... aParams) throws DAOException {
    SqlEditor delete = SqlEditor.forTx(aSqlId, aConnection, aParams);
    return delete.editDatabase();
  }
  
  /**
   <tt>SELECT</tt> operation which typically returns a single item with a <tt>0..N</tt> relation. 
    
   <P>The <tt>ResultSet</tt> is parsed into a single parent Model Object having a <tt>List</tt> of 
   <tt>0..N</tt> child Model Objects. 
   See note on <a href="#CompundObjects">compound objects</a> for more information.
     
   @param aConnection single connection shared by all operations in the transaction.
   @param aClassParent class of the parent Model Object.
   @param aClassChild class of the child Model Object.
   @param aNumTrailingColsForChildList number of columns appearing at the end of the <tt>ResultSet</tt> which 
   are passed to the <em>child</em> constructor.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> to the underlying SQL statement.
  */
  public static <T> T fetchCompound(Connection aConnection, Class<T> aClassParent, Class<?> aClassChild, int aNumTrailingColsForChildList, SqlId aSqlId, Object... aParams) throws DAOException {
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    ModelBuilder<T> builder = new ModelFromRow<T>(aClassParent, aClassChild, aNumTrailingColsForChildList);
    return fetcher.fetchObject(builder);
  }

  /**
   <tt>SELECT</tt> operation which typically returns mutliple items item with a <tt>0..N</tt> relation. 
    
   <P>The <tt>ResultSet</tt> is parsed into a <tt>List</tt> of parent Model Objects, each having <tt>0..N</tt> 
   child Model Objects. See note on <a href="#CompundObjects">compound objects</a> for more information.
     
   @param aConnection single connection shared by all operations in the transaction.
   @param aClassParent class of the parent Model Object.
   @param aClassChild class of the child Model Object.
   @param aNumTrailingColsForChildList number of columns appearing at the end of the <tt>ResultSet</tt> which 
   are passed to the <em>child</em> constructor.
   @param aSqlId identifies the underlying SQL statement.
   @param aParams <a href="#Parameters">parameters</a> to the underlying SQL statement.
 */
  public static <T> List<T> listCompound(Connection aConnection, Class<T> aClassParent, Class<?> aClassChild, int aNumTrailingColsForChildList, SqlId aSqlId, Object... aParams) throws DAOException {
    List<T> result = new ArrayList<T>();
    SqlFetcher fetcher = SqlFetcher.forTx(aSqlId, aConnection, aParams);
    ModelBuilder<T> builder = new ModelFromRow<T>(aClassParent, aClassChild, aNumTrailingColsForChildList);
    fetcher.fetchObjects(builder, result);
    return result;
  }
  
  // PRIVATE 
  
  private DbTx() {
    //prevent construction by the caller
  }
}
