/*
 *    Geotools - OpenSource mapping toolkit
 *
 *    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    This file is based on an origional contained in the GISToolkit project:
 *    http://gistoolkit.sourceforge.net/
 */
package org.geotools.data.shapefile.dbf;

import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.util.TimeZone;

import org.geotools.data.shapefile.ShpFiles;
import org.geotools.resources.NIOUtilities;

/**
 * A DbaseFileReader is used to read a dbase III format file. <br>
 * The general use of this class is: <CODE><PRE>
 * FileChannel in = new FileInputStream(&quot;thefile.dbf&quot;).getChannel();
 * DbaseFileReader r = new DbaseFileReader( in )
 * Object[] fields = new Object[r.getHeader().getNumFields()];
 * while (r.hasNext()) {
 *    r.readEntry(fields);
 *    // do stuff
 * }
 * r.close();
 * </PRE></CODE> For consumers who wish to be a bit more selective with their reading
 * of rows, the Row object has been added. The semantics are the same as using
 * the readEntry method, but remember that the Row object is always the same.
 * The values are parsed as they are read, so it pays to copy them out (as each
 * call to Row.read() will result in an expensive String parse). <br>
 * <b>EACH CALL TO readEntry OR readRow ADVANCES THE FILE!</b><br>
 * An example of using the Row method of reading: <CODE><PRE>
 * FileChannel in = new FileInputStream(&quot;thefile.dbf&quot;).getChannel();
 * DbaseFileReader r = new DbaseFileReader( in )
 * int fields = r.getHeader().getNumFields();
 * while (r.hasNext()) {
 *   DbaseFileReader.Row row = r.readRow();
 *   for (int i = 0; i &lt; fields; i++) {
 *     // do stuff
 *     Foo.bar( row.read(i) );
 *   }
 * }
 * r.close();
 * </PRE></CODE>
 * 
 * @author Ian Schneider
 * @author Tommaso Nolli
 *
 * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/modules/plugin/shapefile/src/main/java/org/geotools/data/shapefile/dbf/IndexedDbaseFileReader.java $
 */
public class IndexedDbaseFileReader extends DbaseFileReader {

    public void goTo(int recno) throws IOException,
            UnsupportedOperationException {

        if (this.randomAccessEnabled) {
            long newPosition = this.header.getHeaderLength()
                    + this.header.getRecordLength() * (long) (recno - 1);

            if (this.useMemoryMappedBuffer) {
                if(newPosition < this.currentOffset || (this.currentOffset + buffer.limit()) < (newPosition + header.getRecordLength())) {
                    NIOUtilities.clean(buffer);
                    FileChannel fc = (FileChannel) channel;
                    if(fc.size() > newPosition + Integer.MAX_VALUE) {
                        currentOffset = newPosition;
                    } else {
                        currentOffset = fc.size() - Integer.MAX_VALUE;
                    }
                    buffer = fc.map(MapMode.READ_ONLY, currentOffset, Integer.MAX_VALUE);
                    buffer.position((int) (newPosition - currentOffset));
                } else {
                    buffer.position((int) (newPosition - currentOffset));
                }
            } else {
                if (this.currentOffset <= newPosition
                        && this.currentOffset + buffer.limit() >= newPosition) {
                    buffer.position((int) (newPosition - this.currentOffset));
                    //System.out.println("Hit");
                } else {
                    //System.out.println("Jump");
                    FileChannel fc = (FileChannel) this.channel;
                    fc.position(newPosition);
                    this.currentOffset = newPosition;
                    buffer.limit(buffer.capacity());
                    buffer.position(0);
                    fill(buffer, fc);
                    buffer.position(0);
                }
            }
        } else {
            throw new UnsupportedOperationException(
                    "Random access not enabled!");
        }

    }
    
    public IndexedDbaseFileReader(ShpFiles shpFiles,
            boolean useMemoryMappedBuffer, Charset stringCharset, TimeZone timeZone)
            throws IOException {
        super(shpFiles, useMemoryMappedBuffer, stringCharset, timeZone);
    }
}
