/**********************************************************************
 * $Id: mitab_tabseamless.cpp,v 1.6 2004/06/30 20:29:04 dmorissette Exp $
 *
 * Name:     mitab_tabseamless.cpp
 * Project:  MapInfo TAB Read/Write library
 * Language: C++
 * Purpose:  Implementation of the TABSeamless class, used to handle seamless
 *           .TAB datasets.
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
 *
 **********************************************************************
 * Copyright (c) 1999-2004, Daniel Morissette
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************
 *
 * $Log: mitab_tabseamless.cpp,v $
 * Revision 1.6  2004/06/30 20:29:04  dmorissette
 * Fixed refs to old address danmo@videotron.ca
 *
 * Revision 1.5  2004/03/12 16:29:05  dmorissette
 * Fixed 2 memory leaks (bug 283)
 *
 * Revision 1.4  2002/06/28 18:32:37  julien
 * Add SetSpatialFilter() in TABSeamless class (Bug 164, MapServer)
 * Use double for comparison in Coordsys2Int() in mitab_mapheaderblock.cpp
 *
 * Revision 1.3  2001/09/19 14:21:36  daniel
 * On Unix: replace '\\' in file path read from tab index with '/'
 *
 * Revision 1.2  2001/03/15 03:57:51  daniel
 * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
 *
 * Revision 1.1  2001/03/09 04:38:04  danmo
 * Update from master - version 1.1.0
 *
 * Revision 1.1  2001/03/09 04:16:02  daniel
 * Added TABSeamless for reading seamless TAB files
 *
 **********************************************************************/

#include "mitab.h"
#include "mitab_utils.h"

#include <ctype.h>      /* isspace() */

/*=====================================================================
 *                      class TABSeamless
 *
 * Support for seamless vector datasets.
 *
 * The current implementation has some limitations (base assumptions):
 *  - Read-only
 *  - Base tables can only be of type TABFile
 *  - Feature Ids are build using the id of the base table in the main
 *    index table and the actual feature id of each object inside the base
 *    tables.  Since we are limited to 32 bits for feature ids, this implies
 *    a limit on the (number of base tables) * (features/table)
 *    The current implementation can support up to 2047 (0x7ff) base tables
 *    and a max of 1048575 (0xfffff) features per base table.
 *  - Only relative paths are supported for base tables names.
 *    
 *====================================================================*/


/**********************************************************************
 *                   TABSeamless::TABSeamless()
 *
 * Constructor.
 **********************************************************************/
TABSeamless::TABSeamless()
{
    m_pszFname = NULL;
    m_pszPath = NULL;
    m_eAccessMode = TABRead;
    m_poFeatureDefnRef = NULL;
    m_poCurFeature = NULL;
    m_nCurFeatureId = -1;
    
    m_poIndexTable = NULL;
    m_nTableNameField = -1;
    m_nCurBaseTableId = -1;
    m_poCurBaseTable = NULL;
    m_bEOF = FALSE;
}

/**********************************************************************
 *                   TABSeamless::~TABSeamless()
 *
 * Destructor.
 **********************************************************************/
TABSeamless::~TABSeamless()
{
    Close();
}


void TABSeamless::ResetReading()
{
    if (m_poIndexTable)
        OpenBaseTable(-1);  // Asking for first table resets everything
}


/**********************************************************************
 *                   TABSeamless::Open()
 *
 * Open a seamless .TAB dataset and initialize the structures to be ready
 * to read features from it.
 *
 * Seamless .TAB files are composed of a main .TAB file in which each 
 * feature is the MBR of a base table.
 *
 * Set bTestOpenNoError=TRUE to silently return -1 with no error message
 * if the file cannot be opened.  This is intended to be used in the
 * context of a TestOpen() function.  The default value is FALSE which
 * means that an error is reported if the file cannot be opened.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::Open(const char *pszFname, const char *pszAccess,
                      GBool bTestOpenNoError /*= FALSE*/ )
{
    char nStatus = 0;
   
    if (m_poIndexTable)
    {
        CPLError(CE_Failure, CPLE_AssertionFailed,
                 "Open() failed: object already contains an open file");
        return -1;
    }

    /*-----------------------------------------------------------------
     * Validate access mode and call the right open method
     *----------------------------------------------------------------*/
    if (EQUALN(pszAccess, "r", 1))
    {
        m_eAccessMode = TABRead;
        nStatus = OpenForRead(pszFname, bTestOpenNoError);
    }
    else
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Open() failed: access mode \"%s\" not supported", pszAccess);
        return -1;
    }

    return nStatus;
}


/**********************************************************************
 *                   TABSeamless::OpenForRead()
 *
 * Open for reading
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::OpenForRead(const char *pszFname, 
                             GBool bTestOpenNoError /*= FALSE*/ )
{
    int nFnameLen = 0;
   
    m_eAccessMode = TABRead;

    /*-----------------------------------------------------------------
     * Read main .TAB (text) file
     *----------------------------------------------------------------*/
    m_pszFname = CPLStrdup(pszFname);

#ifndef _WIN32
    /*-----------------------------------------------------------------
     * On Unix, make sure extension uses the right cases
     * We do it even for write access because if a file with the same
     * extension already exists we want to overwrite it.
     *----------------------------------------------------------------*/
    TABAdjustFilenameExtension(m_pszFname);
#endif

    /*-----------------------------------------------------------------
     * Open .TAB file... since it's a small text file, we will just load
     * it as a stringlist in memory.
     *----------------------------------------------------------------*/
    char **papszTABFile = TAB_CSLLoad(m_pszFname);
    if (papszTABFile == NULL)
    {
        if (!bTestOpenNoError)
        {
            CPLError(CE_Failure, CPLE_FileIO,
                     "Failed opening %s.", m_pszFname);
        }
        
        CPLFree(m_pszFname);
        CSLDestroy(papszTABFile);
        return -1;
    }

    /*-------------------------------------------------------------
     * Look for a metadata line with "\IsSeamless" = "TRUE".
     * If there is no such line, then we may have a valid .TAB file,
     * but we do not support it in this class.
     *------------------------------------------------------------*/
    GBool bSeamlessFound = FALSE;
    for (int i=0; !bSeamlessFound && papszTABFile && papszTABFile[i]; i++)
    {
        const char *pszStr = papszTABFile[i];
        while(*pszStr != '\0' && isspace(*pszStr))
            pszStr++;
        if (EQUALN(pszStr, "\"\\IsSeamless\" = \"TRUE\"", 21))
            bSeamlessFound = TRUE;
    }
    CSLDestroy(papszTABFile);

    if ( !bSeamlessFound )
    {
        if (!bTestOpenNoError)
            CPLError(CE_Failure, CPLE_NotSupported,
                     "%s does not appear to be a Seamless TAB File.  "
                     "This type of .TAB file cannot be read by this library.",
                     m_pszFname);
        else
            CPLErrorReset();

        CPLFree(m_pszFname);

        return -1;
    }

    /*-----------------------------------------------------------------
     * OK, this appears to be a valid seamless TAB dataset...
     * Extract the path component from the main .TAB filename
     * to build the filename of the base tables
     *----------------------------------------------------------------*/
    m_pszPath = CPLStrdup(m_pszFname);
    nFnameLen = strlen(m_pszPath);
    for( ; nFnameLen > 0; nFnameLen--)
    {
        if (m_pszPath[nFnameLen-1] == '/' || 
            m_pszPath[nFnameLen-1] == '\\' )
        {
            break;
        }
        m_pszPath[nFnameLen-1] = '\0';
    }

    /*-----------------------------------------------------------------
     * Open the main Index table and look for the "Table" field that
     * should contain the path to the base table for each rectangle MBR
     *----------------------------------------------------------------*/
    m_poIndexTable = new TABFile;
    if (m_poIndexTable->Open(m_pszFname, "rb", bTestOpenNoError) != 0)
    {
        // Open Failed... an error has already been reported, just return.
        if (bTestOpenNoError)
            CPLErrorReset();
        Close();
        return -1;
    }

    OGRFeatureDefn *poDefn = m_poIndexTable->GetLayerDefn();
    if (poDefn == NULL ||
        (m_nTableNameField = poDefn->GetFieldIndex("Table")) == -1)
    {
        if (!bTestOpenNoError)
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Open Failed: Field 'Table' not found in Seamless "
                     "Dataset '%s'.  This is type of file not currently "
                     "supported.",
                     m_pszFname);
        Close();
        return -1;        
    }

    /*-----------------------------------------------------------------
     * Check number of features in index table to make sure we won't
     * overflow the number of bytes used for table ids in the encoded
     * feature ids.  We currently use 12 bits for table id + 20 bits for FID
     *----------------------------------------------------------------*/
    if (m_poIndexTable->GetFeatureCount(FALSE) > 0x7ff)
    {
        if (!bTestOpenNoError)
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Open Failed: The current implementation is limited "
                     "to 2047 base tables.  The seamless file '%s' contains "
                     "%d tables and cannot be opened.",
                     m_pszFname, m_poIndexTable->GetFeatureCount(FALSE));
        Close();
        return -1;        
    }

    /*-----------------------------------------------------------------
     * We need to open the first table to get its FeatureDefn
     *----------------------------------------------------------------*/
    if (OpenBaseTable(-1, bTestOpenNoError) != 0 )
    {
        // Open Failed... an error has already been reported, just return.
        if (bTestOpenNoError)
            CPLErrorReset();
        Close();
        return -1;
    }

    CPLAssert(m_poCurBaseTable);
    m_poFeatureDefnRef = m_poCurBaseTable->GetLayerDefn();
    m_poFeatureDefnRef->Reference();

    return 0;
}


/**********************************************************************
 *                   TABSeamless::Close()
 *
 * Close current file, and release all memory used.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::Close()
{
    if (m_poIndexTable)
        delete m_poIndexTable;  // Automatically closes.
    m_poIndexTable = NULL;

    if (m_poFeatureDefnRef && m_poFeatureDefnRef->Dereference() == 0)
        delete m_poFeatureDefnRef;
    m_poFeatureDefnRef = NULL;

    if (m_poCurFeature)
        delete m_poCurFeature;
    m_poCurFeature = NULL;
    m_nCurFeatureId = -1;

    CPLFree(m_pszFname);
    m_pszFname = NULL;

    CPLFree(m_pszPath);
    m_pszPath = NULL;

    m_nTableNameField = -1;
    m_nCurBaseTableId = -1;

    if (m_poCurBaseTable)
        delete m_poCurBaseTable;
    m_poCurBaseTable = NULL;

    return 0;
}

/**********************************************************************
 *                   TABSeamless::OpenBaseTable()
 *
 * Open the base table for specified IndexFeature.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::OpenBaseTable(TABFeature *poIndexFeature,
                               GBool bTestOpenNoError /*=FALSE*/)
{
    CPLAssert(poIndexFeature);

    /*-----------------------------------------------------------------
     * Fetch table id.  We actually use the index feature's ids as the
     * base table ids.
     *----------------------------------------------------------------*/
    int nTableId = poIndexFeature->GetFID();

    if (m_nCurBaseTableId == nTableId && m_poCurBaseTable != NULL)
    {
        // The right table is already opened.  Not much to do!
        m_poCurBaseTable->ResetReading();
        return 0;
    }

    // Close current base table
    if (m_poCurBaseTable)
        delete m_poCurBaseTable;
    m_nCurBaseTableId = -1;

    m_bEOF = FALSE;

    /*-----------------------------------------------------------------
     * Build full path to the table and open it.
     * __TODO__ For now we assume that all table filename paths are relative
     *          but we may have to deal with absolute filenames as well.
     *----------------------------------------------------------------*/
    const char *pszName = poIndexFeature->GetFieldAsString(m_nTableNameField);
    char *pszFname = CPLStrdup(CPLSPrintf("%s%s", m_pszPath, pszName));

#ifndef _WIN32
    // On Unix, replace any '\\' in path with '/'
    char *pszPtr = pszFname;
    while((pszPtr = strchr(pszPtr, '\\')) != NULL)
    {
        *pszPtr = '/';
        pszPtr++;
    }
#endif

    m_poCurBaseTable = new TABFile;
    if (m_poCurBaseTable->Open(pszFname, "rb", bTestOpenNoError) != 0)
    {
        // Open Failed... an error has already been reported, just return.
        if (bTestOpenNoError)
            CPLErrorReset();
        delete m_poCurBaseTable;
        m_poCurBaseTable = NULL;
        CPLFree(pszFname);
        return -1;
    }

    // Set the spatial filter to the new table 
    if( m_poFilterGeom != NULL &&  m_poCurBaseTable )
    {
        m_poCurBaseTable->SetSpatialFilter( m_poFilterGeom );
    }

    m_nCurBaseTableId = nTableId;
    CPLFree(pszFname);

    return 0;
}

/**********************************************************************
 *                   TABSeamless::OpenBaseTable()
 *
 * Open the base table for specified IndexFeature.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::OpenBaseTable(int nTableId, GBool bTestOpenNoError /*=FALSE*/)
{

    if (nTableId == -1)
    {
        // Open first table from dataset
        m_poIndexTable->ResetReading();
        if (OpenNextBaseTable(bTestOpenNoError) != 0)
        {
            // Open Failed... an error has already been reported.
            if (bTestOpenNoError)
                CPLErrorReset();
            return -1;
        }
    }
    else if (nTableId == m_nCurBaseTableId && m_poCurBaseTable != NULL)
    {
        // The right table is already opened.  Not much to do!
        m_poCurBaseTable->ResetReading();
        return 0;
    }
    else
    {
        TABFeature *poIndexFeature = m_poIndexTable->GetFeatureRef(nTableId);
    
        if (poIndexFeature)
        {
            if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0)
            {
                // Open Failed... an error has already been reported.
                if (bTestOpenNoError)
                    CPLErrorReset();
                return -1;
            }
        }
    }

    return 0;
}

/**********************************************************************
 *                   TABSeamless::OpenNextBaseTable()
 *
 * Open the next base table in the dataset, using GetNextFeature() so that
 * the spatial filter is respected.
 *
 * m_bEOF will be set if there are no more base tables to read.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::OpenNextBaseTable(GBool bTestOpenNoError /*=FALSE*/)
{
    CPLAssert(m_poIndexTable);

    TABFeature *poIndexFeature = (TABFeature*)m_poIndexTable->GetNextFeature();
    
    if (poIndexFeature)
    {
        if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0)
        {
            // Open Failed... an error has already been reported.
            if (bTestOpenNoError)
                CPLErrorReset();
            delete poIndexFeature;
            return -1;
        }
        delete poIndexFeature;
        m_bEOF = FALSE;
    }
    else
    {
        // Reached EOF
        m_bEOF = TRUE;
    }

    return 0;
}


/**********************************************************************
 *                   TABSeamless::EncodeFeatureId()
 *
 * Combine the table id + feature id into a single feature id that should
 * be unique amongst all base tables in this seamless dataset.
 *
 * We reserve some bits in the feature id for the table id based on the
 * number of features in the index table.  This reduces the available range
 * of feature ids for each base table... for instance, if the index contains
 * 65000 entries, then each base table will be limited to 65535 features.
 *
 * If the number of features in a base table exceeds the number of bits
 * available (e.g. 65535 inthe above example) then the feature ids will
 * wrap for this table and thus it will be impossible to access some of
 * the features unless the caller uses only calls to GetNextFeature() and
 * avoids calls to GetFeatureRef().
 **********************************************************************/
int TABSeamless::EncodeFeatureId(int nTableId, int nBaseFeatureId)
{
    if (nTableId == -1 || nBaseFeatureId == -1)
        return -1;

    /*-----------------------------------------------------------------
     * __TODO__ For now we hardcode the bitmasks to be 12 bits for tableid
     *          and 20 bits for base feature id, but we should really
     *          base the numbers of bits on the number of features in
     *          the index table.
     *----------------------------------------------------------------*/
    return (nTableId*0x00100000 + nBaseFeatureId);
}

int TABSeamless::ExtractBaseTableId(int nEncodedFeatureId)
{
    if (nEncodedFeatureId == -1)
        return -1;

    return (nEncodedFeatureId/0x00100000);
}

int TABSeamless::ExtractBaseFeatureId(int nEncodedFeatureId)
{
    if (nEncodedFeatureId == -1)
        return -1;

    return (nEncodedFeatureId & 0x000fffff);
}

/**********************************************************************
 *                   TABSeamless::GetNextFeatureId()
 *
 * Returns feature id that follows nPrevId, or -1 if it is the
 * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
 **********************************************************************/
int TABSeamless::GetNextFeatureId(int nPrevId)
{
    if (m_poIndexTable == NULL)
        return -1; // File is not opened yet

    if (nPrevId == -1 || m_nCurBaseTableId != ExtractBaseTableId(nPrevId))
    {
        if (OpenBaseTable(ExtractBaseTableId(nPrevId)) != 0)
            return -1;
    }

    int nId = ExtractBaseFeatureId(nPrevId);
    do
    {
        nId = m_poCurBaseTable->GetNextFeatureId(nId);
        if (nId != -1)
            return EncodeFeatureId(m_nCurBaseTableId, nId);  // Found one!
        else
            OpenNextBaseTable();  // Skip to next tile and loop again

    } while (nId == -1 && !m_bEOF && m_poCurBaseTable);

    return -1;
}

/**********************************************************************
 *                   TABSeamless::GetFeatureRef()
 *
 * Fill and return a TABFeature object for the specified feature id.
 *
 * The returned pointer is a reference to an object owned and maintained
 * by this TABSeamless object.  It should not be altered or freed by the 
 * caller and its contents is guaranteed to be valid only until the next
 * call to GetFeatureRef() or Close().
 *
 * Returns NULL if the specified feature id does not exist of if an
 * error happened.  In any case, CPLError() will have been called to
 * report the reason of the failure.
 **********************************************************************/
TABFeature *TABSeamless::GetFeatureRef(int nFeatureId)
{
    if (m_poIndexTable == NULL)
        return NULL; // File is not opened yet

    if (nFeatureId == m_nCurFeatureId && m_poCurFeature)
        return m_poCurFeature;

    if (m_nCurBaseTableId != ExtractBaseTableId(nFeatureId))
    {
        if (OpenBaseTable(ExtractBaseTableId(nFeatureId)) != 0)
            return NULL;
    }

    if (m_poCurBaseTable)
    {
        if (m_poCurFeature)
            delete m_poCurFeature;

        m_poCurFeature = (TABFeature*)m_poCurBaseTable->GetFeature(ExtractBaseFeatureId(nFeatureId));
        m_nCurFeatureId = nFeatureId;

        m_poCurFeature->SetFID(nFeatureId);

        return m_poCurFeature;
    }

    return NULL;
}


/**********************************************************************
 *                   TABSeamless::GetLayerDefn()
 *
 * Returns a reference to the OGRFeatureDefn that will be used to create
 * features in this dataset.
 *
 * Returns a reference to an object that is maintained by this TABSeamless
 * object (and thus should not be modified or freed by the caller) or
 * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
 * opened yet)
 **********************************************************************/
OGRFeatureDefn *TABSeamless::GetLayerDefn()
{
    return m_poFeatureDefnRef;
}

/**********************************************************************
 *                   TABSeamless::GetNativeFieldType()
 *
 * Returns the native MapInfo field type for the specified field.
 *
 * Returns TABFUnknown if file is not opened, or if specified field index is
 * invalid.
 *
 * Note that field ids are positive and start at 0.
 **********************************************************************/
TABFieldType TABSeamless::GetNativeFieldType(int nFieldId)
{
    if (m_poCurBaseTable)
        return m_poCurBaseTable->GetNativeFieldType(nFieldId);

    return TABFUnknown;
}



/**********************************************************************
 *                   TABSeamless::IsFieldIndexed()
 *
 * Returns TRUE if field is indexed, or FALSE otherwise.
 **********************************************************************/
GBool TABSeamless::IsFieldIndexed(int nFieldId)
{
    if (m_poCurBaseTable)
        return m_poCurBaseTable->IsFieldIndexed(nFieldId);

    return FALSE;
}

/**********************************************************************
 *                   TABSeamless::IsFieldUnique()
 *
 * Returns TRUE if field is in the Unique table, or FALSE otherwise.
 **********************************************************************/
GBool TABSeamless::IsFieldUnique(int nFieldId)
{
    if (m_poCurBaseTable)
        return m_poCurBaseTable->IsFieldUnique(nFieldId);

    return FALSE;
}


/**********************************************************************
 *                   TABSeamless::GetBounds()
 *
 * Fetch projection coordinates bounds of a dataset.
 *
 * The bForce flag has no effect on TAB files since the bounds are
 * always in the header.
 *
 * Returns 0 on success, -1 on error.
 **********************************************************************/
int TABSeamless::GetBounds(double &dXMin, double &dYMin, 
                       double &dXMax, double &dYMax,
                       GBool bForce /*= TRUE*/)
{
    if (m_poIndexTable == NULL)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
             "GetBounds() can be called only after dataset has been opened.");
        return -1;
    }

    return m_poIndexTable->GetBounds(dXMin, dYMin, dXMax, dYMax, bForce);
}

/**********************************************************************
 *                   TABSeamless::GetExtent()
 *
 * Fetch extent of the data currently stored in the dataset.
 *
 * The bForce flag has no effect on TAB files since that value is
 * always in the header.
 *
 * Returns OGRERR_NONE/OGRRERR_FAILURE.
 **********************************************************************/
OGRErr TABSeamless::GetExtent (OGREnvelope *psExtent, int bForce)
{
    if (m_poIndexTable == NULL)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
             "GetExtent() can be called only after dataset has been opened.");
        return OGRERR_FAILURE;
    }

    return m_poIndexTable->GetExtent(psExtent, bForce);

}

/**********************************************************************
 *                   TABSeamless::GetFeatureCountByType()
 *
 * Return number of features of each type.
 *
 * Note that the sum of the 4 returned values may be different from
 * the total number of features since features with NONE geometry
 * are not taken into account here.
 *
 * Returns 0 on success, or silently returns -1 (with no error) if this
 * information is not available.
 **********************************************************************/
int TABSeamless::GetFeatureCountByType(int &numPoints, int &numLines,
                                   int &numRegions, int &numTexts,
                                   GBool bForce /*= TRUE*/)
{
    /*-----------------------------------------------------------------
     * __TODO__  This should be implemented to return -1 if force=false,
     * or scan all the base tables if force=true
     *----------------------------------------------------------------*/

    return -1;
}

int TABSeamless::GetFeatureCount(int bForce)
{
    /*-----------------------------------------------------------------
     * __TODO__  This should be implemented to return -1 if force=false,
     * or scan all the base tables if force=true
     *----------------------------------------------------------------*/

    return OGRLayer::GetFeatureCount(bForce);
}


/**********************************************************************
 *                   TABSeamless::GetSpatialRef()
 *
 * Returns a reference to an OGRSpatialReference for this dataset.
 * If the projection parameters have not been parsed yet, then we will
 * parse them before returning.
 *
 * The returned object is owned and maintained by this TABFile and
 * should not be modified or freed by the caller.
 *
 * Returns NULL if the SpatialRef cannot be accessed.
 **********************************************************************/
OGRSpatialReference *TABSeamless::GetSpatialRef()
{
    if (m_poIndexTable == NULL)
    {
        CPLError(CE_Failure, CPLE_AssertionFailed,
                 "GetSpatialRef() failed: file has not been opened yet.");
        return NULL;
    }

    return m_poIndexTable->GetSpatialRef();
}



/**********************************************************************
 *                   IMapInfoFile::SetSpatialFilter()
 *
 * Standard OGR SetSpatialFiltere implementation.  This methode is used
 * to set a SpatialFilter for this OGRLayer
 **********************************************************************/
void TABSeamless::SetSpatialFilter (OGRGeometry * poGeomIn )

{
    IMapInfoFile::SetSpatialFilter( poGeomIn );

    if( m_poIndexTable )
        m_poIndexTable->SetSpatialFilter( poGeomIn );

    if( m_poCurBaseTable )
        m_poCurBaseTable->SetSpatialFilter( poGeomIn );
}



/************************************************************************/
/*                           TestCapability()                           */
/************************************************************************/

int TABSeamless::TestCapability( const char * pszCap )

{
    if( EQUAL(pszCap,OLCRandomRead) )
        return TRUE;

    else if( EQUAL(pszCap,OLCSequentialWrite) 
             || EQUAL(pszCap,OLCRandomWrite) )
        return FALSE;

    else if( EQUAL(pszCap,OLCFastFeatureCount) )
        return FALSE;

    else if( EQUAL(pszCap,OLCFastSpatialFilter) )
        return FALSE;

    else if( EQUAL(pszCap,OLCFastGetExtent) )
        return TRUE;

    else 
        return FALSE;
}



/**********************************************************************
 *                   TABSeamless::Dump()
 *
 * Dump block contents... available only in DEBUG mode.
 **********************************************************************/
#ifdef DEBUG

void TABSeamless::Dump(FILE *fpOut /*=NULL*/)
{
    if (fpOut == NULL)
        fpOut = stdout;

    fprintf(fpOut, "----- TABSeamless::Dump() -----\n");

    if (m_poIndexTable == NULL)
    {
        fprintf(fpOut, "File is not opened.\n");
    }
    else
    {
        fprintf(fpOut, "File is opened: %s\n", m_pszFname);

    }

    fflush(fpOut);
}

#endif // DEBUG


