package uk.ac.starlink.table;

import java.io.IOException;

/**
 * Wrapper table which provides a view of a base table in which the 
 * rows are permuted.  Each row in the wrapper table is a view
 * of one in the base table.  It is permitted for the wrapper to contain
 * multiple views of the same column in the base.
 * <p>
 * A <code>long[]</code> array, <code>rowMap</code>, is used to keep
 * track of which rows in this table correspond to which rows in the
 * base table; the <tt>n</tt>'th row in this table corresponds to the
 * <tt>rowMap[n]</tt>'th row in the base table.
 * The <tt>rowMap</tt> array may contain duplicate entries, but should
 * not contain any entries larger than the number of rows in the base table.
 * Any negative entry is treated as a special case resulting in a 'blank'
 * row of all null values.
 * It can be modified during the life of the table, but it's not a good
 * idea to do this while a <tt>RowSequence</tt> is active.
 *
 * @author   Mark Taylor (Starlink)
 */
public class RowPermutedStarTable extends WrapperStarTable {

    private long[] rowMap;

    /**
     * Constructs a new <tt>RowPermutedStarTable</tt> from a base table
     * and a <tt>rowMap</tt> array.
     * <p>
     * <tt>baseTable</tt> must provide random access.
     *
     * @param  baseTable  base table
     * @param  rowMap     array mapping rows in the new permuted table to 
     *                    rows in <tt>baseTable</tt>
     * @throws IllegalArgumentException  if <tt>baseTable.isRandom</tt> returns
     *         <tt>false</tt>
     */
    public RowPermutedStarTable( StarTable baseTable, long[] rowMap ) {
        super( baseTable );
        if ( ! baseTable.isRandom() ) {
            throw new IllegalArgumentException( "No random access in base " +
                                                "table " + baseTable );
        }
        this.rowMap = rowMap;
    }

    /**
     * Constructs a new RowPermutedTable with rows initially in unpermuted
     * order.
     *
     * @param  baseTable  base table
     */
    public RowPermutedStarTable( StarTable baseTable ) {
        this( baseTable, 
              new long[ Math.max( checkedLongToInt( baseTable.getRowCount() ),
                                  0 ) ] );
        int nrow = rowMap.length;
        for ( int i = 0; i < nrow; i++ ) {
            rowMap[ i ] = i;
        }
    }

    /**
     * Returns the mapping array.
     * 
     * @return  array mapping rows in this table to rows in the base table
     */
    public long[] getRowMap() {
        return rowMap;
    }

    /**
     * Sets the mapping array.
     *
     * @param  rowMap array mapping rows in this table to rows in the base table
     */
    public void setRowMap( long[] rowMap ) {
        this.rowMap = rowMap;
    }

    public long getRowCount() {
        return (long) rowMap.length;
    }

    /**
     * Returns true.
     */
    public boolean isRandom() {
        return true;
    }

    public RowSequence getRowSequence() {
        return new RandomRowSequence( this );
    }

    public Object getCell( long irow, int icol ) throws IOException {
        long baseRow = rowMap[ checkedLongToInt( irow ) ];
        return baseRow >= 0 ? baseTable.getCell( baseRow, icol )
                            : null;
    }

    public Object[] getRow( long irow ) throws IOException {
        long baseRow = rowMap[ checkedLongToInt( irow ) ];
        return baseRow >= 0 ? baseTable.getRow( baseRow )
                            : new Object[ baseTable.getColumnCount() ];
    }
}
